﻿'Description:正式排产-有限产能排产类
'Copyright (c) : 通力凯顿（北京）系统集成有限公司
'Writer:Wangjh 
'create Date:20220511
'Rewriter:
'Rewrite Date:

Imports DatabaseAccess.DBUtility
Imports BusinessRule.DTO
Imports System.Text
Imports External.APICalling
Imports BusinessRule.Common
Imports Newtonsoft.Json
Imports System.Collections.Concurrent
Imports System.Threading.Tasks


Public Class LimitedCapacityBusiness
    Inherits LimitedScheduleBaseBusiness
    Private m_containerObj As New uLMSContainerBusiness

#Region "属性"
    Protected recursionStartTime As Date = Date.Now '递归时间标记，用于防止无限递归 

    Protected Overrides Property Sql_ConApsSelect As String = <string> select * from ApsPlanInfo ai where ai.containerid='{0}' and ai.workflowid='{1}' and ai.ApsType2=2 and ai.ApsType='{2}' and ai.Isuse=0 </string>.Value
    Protected Overrides Property Sql_ConApsInsert As String = <STIRNG> insert into ApsPlanInfo ai (ai.ApsType2,ai.apsplaninfoid,ai.apstype,ai.containerid,ai.createdate,ai.createemployeeid,ai.endtime,ai.isuse,
 ai.lastchangedate,ai.notes,ai.starttime,ai.workflowid) values (2,'{0}','{1}','{2}',sysdate,'{3}',to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,
 SYSDATE,'{5}',to_date('{6}','YYYY-MM-DD HH24:MI:SS'),'{7}') </STIRNG>.Value
    Protected Overrides Property Sql_ConApsUpdate As String = <string>update ApsPlanInfo ai set ai.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS') , ai.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'), 
 ai.lastchangedate=sysdate where ai.apsplaninfoid='{2}'</string>.Value

    ''' <summary>
    ''' 工序级排产信息查询sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsSelect As String = <string>select * from ApsPlanDetail ad where ad.apsplaninfoid='{0}' and ad.ContainerID='{1}' and ad.isuse=0 </string>.Value

    ''' <summary>
    ''' 工序级排产信息插入sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsInsert As String = <string> insert into ApsPlanDetail ad (ad.CreateDate,ad.LastChangeDate,ad.apsplandetailid,ad.apsplaninfoid,ad.containerid,ad.starttime,ad.endtime,ad.isuse,
ad.workflowstepid,ad.workflowstepname,ad.notes,ad.workcenterid,ad.teamid,ad.resourceid,CreateEmployeeID,ad.SpecFactoryID,ad.Resstarttime,ad.Resendtime,ad.qty,ad.status,ad.SpecID) values (sysdate,sysdate,'{0}','{1}','{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,'{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}',to_date('{13}','YYYY-MM-DD HH24:MI:SS'),to_date('{14}','YYYY-MM-DD HH24:MI:SS'),{15},0,'{16}') </string>.Value

    Protected Property Sql_StepApsTempInsert As String = <string> ad (ad.CreateDate,ad.LastChangeDate,ad.apsplandetailid,ad.apsplaninfoid,ad.containerid,ad.starttime,ad.endtime,ad.isuse,
ad.workflowstepid,ad.workflowstepname,ad.notes,ad.workcenterid,ad.teamid,ad.resourceid,CreateEmployeeID,ad.SpecFactoryID,ad.Resstarttime,ad.Resendtime,ad.qty,ad.status,ad.SpecID) values (sysdate,sysdate,'{0}','{1}','{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,'{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}',to_date('{13}','YYYY-MM-DD HH24:MI:SS'),to_date('{14}','YYYY-MM-DD HH24:MI:SS'),{15},0,'{16}')</string>.Value

    ''' <summary>
    ''' 工序级排产信息更新sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsUpdate As String = <string> update ApsPlanDetail ad set  ad.FeedbackDate=null,ad.LastChangeDate=sysdate,ad.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS'),ad.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
ad.workcenterid='{2}',ad.teamid='{3}',ad.resourceid='{4}',ad.SpecFactoryID='{5}',ad.Resstarttime=to_date('{6}','YYYY-MM-DD HH24:MI:SS'),ad.Resendtime=to_date('{7}','YYYY-MM-DD HH24:MI:SS'),ad.qty={8} where ad.apsplaninfoid='{9}' and ad.workflowstepid='{10}' and apsplandetailid='{11}' </string>.Value
    ''' <summary>
    ''' 工序级排产信息删除sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsDel As String = <string>delete ApsPlanDetail ae where ae.apsplandetailid='{0}'</string>.Value

    ''' <summary>
    ''' 工序级排产信息范围计算sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsRange As String = <string>select ad.containerid,min(ad.starttime) minstarttime,max(ad.endtime) maxendtime from ApsPlanDetail ad where ad.apsplaninfoid='{0}' and ad.isuse=0 group by ad.containerid </string>.Value

    ''' <summary>
    ''' 工序级排产人员信息删除sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_StepEmpApsDel As String = <string>delete ApsPlanDetailemp ae where ae.apsplandetailid='{0}'</string>.Value
    ''' <summary>
    ''' 工序级排产人员信息插入sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_StepEmpApsInsert As String = <string>insert into ApsPlanDetailemp (Apsplandetailid,Employeeid,Isuse,Apsplandetailempid) values ('{0}','{1}',0,'{2}')</string>.Value

    ''' <summary>
    ''' 工序级排产人员时间信息删除sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_EmpTimeApsDel As String = <string>delete from apsplandetailemptime where apsplandetailempid in (select apsplandetailempid from apsplandetailemp where apsplandetailid='{0}') </string>.Value
    ''' <summary>
    ''' 工序级排产人员时间信息插入sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_EmpTimeApsInsert As String = <string>insert into apsplandetailemptime  (apsplandetailempid, apsplandetailemptimeid,starttime, isuse, endtime, employeeid) values ('{0}', '{1}', to_date('{2}','yyyy/mm/dd hh24:mi:ss'), '0', to_date('{3}','yyyy/mm/dd hh24:mi:ss'),'{4}')</string>.Value

#End Region
    '-------------------------重写的方法

    ''' <summary>
    ''' 获取设备占用信息，指定时间之后
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="conIdList"></param>
    ''' <param name="startTime"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Function GetResOccupyDtAfterTime(ByVal resIds As String, ByVal conIdList As List(Of String), ByVal startTime As String, ByVal apsType As String, Optional apsTempTable As String = "") As DataTable
        Dim resOccupyAps = "select ad.* from ApsPlanDetail ad where ad.ResourceID in ({0}) and ad.isuse=0 and ad.Status=1 " +
           "and ( ad.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           "or ( " +
           "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           ") ) and not exists (select 1 from containersteprecord csr where csr.containerid=ad.containerid and csr.workflowstepid=ad.workflowstepid " +
           "and csr.usecompleted=1) " +
           " and not exists (select 1 from resourcedef rd where rd.resourceid=ad.resourceid and rd.isinfinite=1) "

        Dim resOccupyAps2 = " select ad.* from ApsPlanDetail ad " +
            " left join ApsPlanInfo ap on ap.apsplaninfoid=ad.apsplaninfoid " +
            " where ap.apstype2=2 and ad.status=0 and ap.apstype='{3}' and ap.containerid in ({2}) and ad.ResourceID in ({0}) " +
            "and ( ad.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           "or ( " +
           "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           ") )" +
            " and not exists (select 1 from resourcedef rd where rd.resourceid=ad.resourceid and rd.isinfinite=1) "
        '运用临时表 add:wangjh 20240830
        Dim resOccupyAps3 = " select ad.* from " + apsTempTable + " ad " +
            " where ad.ResourceID in ({0}) " +
            "and ( ad.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           "or ( " +
           "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           ") )" +
            " and not exists (select 1 from resourcedef rd where rd.resourceid=ad.resourceid and rd.isinfinite=1) "

        Dim result As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, resIds, startTime))

        'conIdList超过1000的处理方式,按999进行分页
        'If conIdList.Count > 999 Then
        '    Dim pCount = Math.Ceiling(conIdList.Count / 999) '页数
        '    For i As Integer = 0 To pCount - 1
        '        Dim _conIdList = conIdList.Skip(i * 999).Take(999).ToList
        '        result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps2, resIds, startTime, "'" + String.Join(",", _conIdList).Replace(",", "','") + "'", apsType)))
        '    Next
        'Else
        '    result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps2, resIds, startTime, "'" + String.Join(",", conIdList).Replace(",", "','") + "'", apsType)))
        'End If


        If Not String.IsNullOrWhiteSpace(apsTempTable) Then
            result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps3, resIds, startTime)))
        End If

        Return result
    End Function

    ''' <summary>
    ''' 获取设备占用信息，指定时间之前
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="conIds"></param>
    ''' <param name="endTime"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Function GetResOccupyDtBeforeTime(ByVal resIds As String, ByVal conIdList As List(Of String), ByVal endTime As String, ByVal apsType As String) As DataTable
        Dim resOccupyAps = "select ad.* from ApsPlanDetail ad where ad.ResourceID in ({0}) and ad.isuse=0 and ad.Status=1 " +
"and ( ad.Resendtime<= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
"or ( " +
"ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
") ) and not exists (select 1 from containersteprecord csr where csr.containerid=ad.containerid and csr.workflowstepid=ad.workflowstepid " +
"and csr.usecompleted=1)" +
" and not exists (select 1 from resourcedef rd where rd.resourceid=ad.resourceid and rd.isinfinite=1) "

        Dim resOccupyAps2 = " select ad.* from ApsPlanDetail ad " +
            " left join ApsPlanInfo ap on ap.apsplaninfoid=ad.apsplaninfoid " +
            " where ap.apstype2=2 and ad.status=0 and ap.apstype='{3}' and ap.containerid in ({2}) and ad.ResourceID in ({0})  " +
            "and ( ad.Resendtime<= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
"or ( " +
"ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
") )" +
            " and not exists (select 1 from resourcedef rd where rd.resourceid=ad.resourceid and rd.isinfinite=1) "

        Dim result As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, resIds, endTime))

        'conIdList超过1000的处理方式,按999进行分页
        If conIdList.Count > 999 Then
            Dim pCount = Math.Ceiling(conIdList.Count / 999) '页数
            For i As Integer = 0 To pCount - 1
                Dim _conIdList = conIdList.Skip(i * 999).Take(999).ToList
                result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps2, resIds, endTime, "'" + String.Join(",", _conIdList).Replace(",", "','") + "'", apsType)))
            Next
        Else
            result.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps2, resIds, endTime, "'" + String.Join(",", conIdList).Replace(",", "','") + "'", apsType)))
        End If

        Return result
    End Function

    ''' <summary>
    ''' 子卡排产的开始结束时间，取子卡对应主卡子工序的排产开始结束时间
    ''' </summary>
    ''' <param name="sonConDt"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function GetSonContainerStartEndDate(ByVal sonConDt As DataTable, ByVal apsType As String) As DataTable
        Dim strSql = <String>select ad.containerid,ai.apstype,TO_CHAR(ad.starttime,'YYYY-MM-DD hh24:mi:ss') starttime,TO_CHAR(ad.endtime,'YYYY-MM-DD hh24:mi:ss') endtime from containersubcontainer cc 
left join apsplandetail ad on ad.containerid=cc.containerid and ad.workflowstepname=cc.workflowstepname and ad.isuse=0 
left join apsplaninfo ai on ai.apsplaninfoid=ad.apsplaninfoid
where cc.subcontainerid='{0}'</String>.Value  '子卡对应父工序的排产计划信息
        For Each sonRow In sonConDt.Rows
            Dim faDate = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, sonRow("containerid").ToString))
            If faDate.Rows.Count > 0 Then
                Dim temp = faDate.Select("apsType='" + apsType + "'")
                If temp.Length > 0 Then
                    sonRow("STARTTIME") = temp(0)("STARTTIME")
                    sonRow("ENDTIME") = temp(0)("ENDTIME")
                Else
                    sonRow("STARTTIME") = faDate.Rows(0)("STARTTIME")
                    sonRow("ENDTIME") = faDate.Rows(0)("ENDTIME")
                End If
            Else
                sonRow("STARTTIME") = DBNull.Value
                sonRow("ENDTIME") = DBNull.Value
            End If
        Next
        sonConDt.AcceptChanges()
        Return sonConDt
    End Function

    ''' <summary>
    ''' 删除排产数据
    ''' </summary>
    ''' <param name="conApsID">排产ID</param>
    ''' <param name="seq">工序顺序</param>
    ''' <remarks></remarks>
    Public Overrides Sub DeleteApsPlanData(ByVal conApsID As String, ByVal seq As Integer, Optional ByVal status As String = "")
        Dim strSql = <string> select al.apsplandetailid from ApsPlanDetail al 
left join workflowstep ws on ws.workflowstepid=al.workflowstepid
where al.apsplaninfoid='{0}' and al.isuse=0 and nvl(al.status,'0')='0' and ws.sequence>={1} </string>.Value
        If Not String.IsNullOrWhiteSpace(status) Then
            strSql += " and al.status='" + status + "'"
        End If
        Dim sqlList As New ArrayList

        Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, conApsID, seq))

        For Each row In apsDetailDt.Rows
            sqlList.Add(String.Format("delete apsplandetailemptime ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp ae where ae.apsplandetailid='{0}')", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete apsplandetailemp ae where ae.apsplandetailid='{0}'", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete apsplandetail al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
        Next

        DatabaseHelper.ExecuteSqlTran(sqlList)

        If DatabaseHelper.GetSingle("select 1 from ApsPlanDetail al where al.apsplaninfoid='" + conApsID + "'") Is Nothing Then
            DatabaseHelper.ExecuteSql("delete ApsPlanInfo where ApsPlanInfoid='" + conApsID + "'")
        End If

    End Sub

    ''' <summary>
    ''' 删除排产数据
    ''' </summary> 
    ''' <remarks></remarks>
    Public Overrides Sub DeleteApsPlanData(ByVal containerId As String, ByVal stepIds As String, Optional ByVal type As String = "1")
        Dim strSql = <string> select ai.apsplaninfoid,al.apsplandetailid from apsplaninfo ai
left join ApsPlanDetail al on al.apsplaninfoid=ai.apsplaninfoid 
where ai.containerId='{0}' and ai.isuse=0 and al.isuse=0 and ai.apstype='{1}'</string>.Value

        strSql = String.Format(strSql, containerId, type)
        strSql += String.Format(" and al.workflowstepid in ({0}) ", stepIds)
        strSql += " and nvl(al.status,'0')='0' "

        Dim sqlList As New ArrayList

        Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(strSql)

        If apsDetailDt.Rows.Count = 0 Then
            Return
        End If

        For Each row In apsDetailDt.Rows
            sqlList.Add(String.Format("delete apsplandetailemptime ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp ae where ae.apsplandetailid='{0}')", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete apsplandetailemp ae where ae.apsplandetailid='{0}'", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete apsplandetail al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
        Next

        DatabaseHelper.ExecuteSqlTran(sqlList)

        If DatabaseHelper.GetSingle("select 1 from ApsPlanDetail al where al.apsplaninfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'") Is Nothing Then
            DatabaseHelper.ExecuteSql("delete ApsPlanInfo where ApsPlanInfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'")
        End If


    End Sub

    ''' <summary>
    ''' 删除排产数据
    ''' </summary> 
    ''' <remarks></remarks>
    Public Overrides Sub DeleteApsPlanData(ByVal apsplandetailIds As List(Of String))

        For Each id As String In apsplandetailIds
            Dim strSql = <string> select ai.apsplaninfoid,al.apsplandetailid from apsplaninfo ai
left join ApsPlanDetail al on al.apsplaninfoid=ai.apsplaninfoid 
where  ai.isuse=0 and al.isuse=0 </string>.Value

            strSql += String.Format(" and al.apsplandetailid='{0}' ", id)
            strSql += " and nvl(al.status,'0')='0' "

            Dim sqlList As New ArrayList

            Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(strSql)

            If apsDetailDt.Rows.Count = 0 Then
                Return
            End If

            For Each row In apsDetailDt.Rows
                sqlList.Add(String.Format("delete apsplandetailemptime ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp ae where ae.apsplandetailid='{0}')", row("apsplandetailid").ToString))
                sqlList.Add(String.Format("delete apsplandetailemp ae where ae.apsplandetailid='{0}'", row("apsplandetailid").ToString))
                sqlList.Add(String.Format("delete ApsPlanDetail al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
            Next

            DatabaseHelper.ExecuteSqlTran(sqlList)

            If DatabaseHelper.GetSingle("select 1 from ApsPlanDetail al where al.apsplaninfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'") Is Nothing Then
                DatabaseHelper.ExecuteSql("delete ApsPlanInfo where ApsPlanInfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'")
            End If

        Next



    End Sub
    '----------------------------------------------

    ''' <summary>
    ''' 有限产能排产，添加排产池批次数据查询
    ''' add:Wangjh 20220511
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetApsSelectContainer(ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim strSql = <string>SELECT CCI.MFGORDERNAME,CCI.CONTAINERID,CCI.CONTAINERNAME,CCI.PRODUCTDESCRIPTION PRODUCTDESC,CCI.PRODUCTREVISION,CCI.QTY,CCI.WORKFLOWSTEPNAME,
CCI.CONTAINERTYPE,DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,
W.WORKFLOWID,WB.WORKFLOWNAME,W.WORKFLOWREVISION,WB.WORKFLOWNAME||':'||W.WORKFLOWREVISION WORKFLOWINFO,pc.prioritycodename,
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') MFGENDDDATE,to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate,to_char(cci.materialdate,'yyyy-mm-dd hh24:mi:ss') materialdate 
FROM CONTAINERCURRENTINFO CCI
LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
LEFT JOIN ORDERSTATUS o ON o.orderstatusid = M.orderstatusid
LEFT JOIN Prioritycode pc ON pc.prioritycodeid = m.priorityid
LEFT JOIN CONTAINER C ON C.CONTAINERID=CCI.CONTAINERID
left join WORKFLOW W ON W.WORKFLOWID=DECODE(CCI.ISINSTORE,-1,C.MFGWORKFLOWID,CCI.WORKFLOWID)
LEFT JOIN WORKFLOWBASE WB ON WB.WORKFLOWBASEID=W.WORKFLOWBASEID
WHERE CCI.CONTAINERSTATUS=1  and nvl(cci.IsInPool,'0')='0' AND CCI.WORKFLOWSTEPNAME!='工艺确认' and cci.isinstore!=1</string>.Value
        'yuxj20221024   取消AND CCI.DISPATCHNUM=0
        strSql += " and (o.orderstatusid IS NULL OR o.ORDERSTATUSNAME <> '关闭') " '订单状态

        If para.ContainsKey("ContainerName") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerName")) Then
                strSql += String.Format(" and cci.ContainerName like '%{0}%' ", para("ContainerName"))
            End If
        End If

        If para.ContainsKey("ProductName") Then
            If Not String.IsNullOrWhiteSpace(para("ProductName")) Then
                strSql += String.Format(" and cci.ProductName like '%{0}%' ", para("ProductName"))
            End If
        End If

        If para.ContainsKey("MfgorderName") Then
            If Not String.IsNullOrWhiteSpace(para("MfgorderName")) Then
                strSql += String.Format(" and cci.MfgorderName like '%{0}%' ", para("MfgorderName"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and cci.originalfactoryid ='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("OrderStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate >= to_date('{0}','YYYY-MM-DD') ", para("OrderStartDate"))
            End If
        End If

        If para.ContainsKey("OrderEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEndDate"))
            End If
        End If

        Dim paraPage As New uMESPagingDataDTO
        paraPage.CurrentPageIndex = Convert.ToInt32(para("CurrentPageIndex"))
        paraPage.PageSize = Convert.ToInt32(para("PageSize").ToString)
        paraPage.strSQL = strSql
        Dim result As uMESProcessResult = DatabaseHelper.RunProcedureForPage(paraPage)
        Return result
    End Function

    ''' <summary>
    ''' 移入批次到排产池
    ''' </summary>
    ''' <param name="containers"></param>
    ''' <remarks></remarks>
    Public Sub MoveContainerToAps(ByVal containers As DataTable)
        Dim sqlList As New ArrayList
        Dim sql = "update containercurrentinfo cci set cci.isinpool=1,cci.expectmaterialdate=to_date('{0}','yyyy/mm/dd') where cci.containerid='{1}' "
        For Each row As DataRow In containers.Rows
            sqlList.Add(String.Format(sql, row("expectmaterialdate").ToString, row("containerid").ToString))
        Next
        If sqlList.Count > 0 Then
            DatabaseHelper.ExecuteSqlTran(sqlList)
        End If
    End Sub

    ''' <summary>
    ''' 移除排产池
    ''' </summary>
    ''' <param name="containers"></param>
    ''' <remarks></remarks>
    Public Sub RemoveApsPool(ByVal containers As DataTable)
        Dim sqlList As New ArrayList
        Dim sql = "update containercurrentinfo cci set cci.isinpool=0 where cci.containerid='{0}' "
        For Each row As DataRow In containers.Rows
            sqlList.Add(String.Format(sql, row("containerid").ToString))
        Next
        If sqlList.Count > 0 Then
            DatabaseHelper.ExecuteSqlTran(sqlList)
        End If
    End Sub

    ''' <summary>
    ''' 有限排产查询sql语句
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetApsPoolContainerSql(ByVal para As Dictionary(Of String, String)) As String
        Dim strSql = <string>SELECT distinct CCI.MFGORDERNAME,CCI.CONTAINERID,CCI.CONTAINERNAME,CCI.PRODUCTDESCRIPTION PRODUCTDESC,CCI.PRODUCTREVISION,CCI.QTY,CCI.WORKFLOWSTEPNAME,
CCI.CONTAINERTYPE,DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,CCI.PRODUCTNAME,CCI.PRODUCTID,
W.WORKFLOWID,WB.WORKFLOWNAME,W.WORKFLOWREVISION,WB.WORKFLOWNAME||':'||W.WORKFLOWREVISION WORKFLOWINFO,wc.apspoolid,
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') MFGENDDDATE,to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate,
to_char(cci.materialdate,'yyyy-mm-dd hh24:mi:ss') materialdate ,TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') REALSTARTDATE, 
nvl2(cc.containersubcontainerid,'1','') ISSUBCON,cci.togetherflag,pc.prioritycodename,cci.CriticalValue,cci.StepDuration,cci.ApsSequence,decode(cci.ApsProPre,1,'是','否') IsPrepare ,
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') STARTTIME,2 DJDKSPECIAL,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') ENDTIME,decode(cci.ISINSTORE,-1,0,cci.workflowstepsequence-1) beginsequence,
ctl.logmessage,tm.teamname,wct.workcentername
FROM CONTAINERCURRENTINFO CCI
left join apspoolworkcenter wc on cci.fromworkcenterid=wc.workcenterid
left join apspooljhyemp ae on ae.apspoolid=wc.apspoolid 
left join apspool ap on ae.apspoolid=ap.apspoolid and ap.isuse=0     
LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
left join prioritycode pc on pc.prioritycodeid=m.priorityid
LEFT JOIN ORDERSTATUS o ON o.orderstatusid = M.orderstatusid
LEFT JOIN CONTAINER C ON C.CONTAINERID=CCI.CONTAINERID
left join WORKFLOW W ON W.WORKFLOWID=DECODE(CCI.ISINSTORE,-1,C.MFGWORKFLOWID,CCI.WORKFLOWID)
LEFT JOIN WORKFLOWBASE WB ON WB.WORKFLOWBASEID=W.WORKFLOWBASEID
left join ContainerScheduledTaskLogs ctl on ctl.containerid=cci.containerid and ctl.islatest=1 and ctl.tasktype=1
left join containersubcontainer cc on cc.subcontainerid=cci.containerid and rownum=1
LEFT JOIN productteaminfo pti ON pti.productname = cci.productname AND pti.isuse = 0
LEFT JOIN team tm ON tm.teamid = pti.teamid
LEFT JOIN workcenter wct ON wct.workcenterid = tm.workcenterid
WHERE CCI.CONTAINERSTATUS=1 and cci.isinpool=1 and cci.isinstore != 1</string>.Value

        'yuxj20221024   取消AND CCI.DISPATCHNUM=0
        strSql += " and (o.orderstatusid IS NULL OR o.ORDERSTATUSNAME <> '关闭') " '订单状态

        If para.ContainsKey("IsPrepare") Then
            If Not String.IsNullOrWhiteSpace(para("IsPrepare")) Then
                strSql += String.Format(" and nvl(cci.ApsProPre,0)={0} ", para("IsPrepare"))
            End If
        End If

        If para.ContainsKey("ContainerName") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerName")) Then
                strSql += String.Format(" and cci.ContainerName like '%{0}%' ", para("ContainerName"))
            End If
        End If

        'yuxj20230412 增加是否已派的判断 0:全部 1:已排 2:未排
        If para.ContainsKey("IsAps") Then
            If para("IsAps") = "1" Then
                strSql += " AND EXISTS  (SELECT 1 FROM APSPLANINFO ai WHERE ai.CONTAINERID=cci.containerid AND ai.isuse=0) "
            ElseIf para("IsAps") = "2" Then
                strSql += " AND not EXISTS  (SELECT 1 FROM APSPLANINFO ai WHERE ai.CONTAINERID=cci.containerid AND ai.isuse=0) "
            End If
        End If

        If para.ContainsKey("ProductName") Then
            If Not String.IsNullOrWhiteSpace(para("ProductName")) Then
                strSql += String.Format(" and cci.ProductName like '%{0}%' ", para("ProductName"))
            End If
        End If

        If para.ContainsKey("MfgorderName") Then
            If Not String.IsNullOrWhiteSpace(para("MfgorderName")) Then
                strSql += String.Format(" and cci.MfgorderName like '%{0}%' ", para("MfgorderName"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and cci.originalfactoryid ='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("PoolID") Then '排产池
            If Not String.IsNullOrWhiteSpace(para("PoolID")) Then
                strSql += String.Format(" and ap.apspoolid ='{0}' ", para("PoolID"))
            End If

            If para.ContainsKey("EmployeeID") Then '根据计划员筛选排产池
                If Not String.IsNullOrWhiteSpace(para("EmployeeID")) Then
                    strSql += String.Format(" and ae.EmployeeID = '{0}' ", para("EmployeeID"))
                End If
            End If
        End If

        '开始时间设定范围
        If para.ContainsKey("OrderBStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderBStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate >= to_date('{0}','YYYY-MM-DD') ", para("OrderBStartDate"))
            End If
        End If

        If para.ContainsKey("OrderEStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEStartDate"))
            End If
        End If
        '结束时间设定范围
        If para.ContainsKey("OrderBEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderBEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate >= to_date('{0}','YYYY-MM-DD') ", para("OrderBEndDate"))
            End If
        End If

        If para.ContainsKey("OrderEEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEEndDate"))
            End If
        End If

        '手动指定日期
        If para.ContainsKey("RealBStartDate2") Then
            If Not String.IsNullOrWhiteSpace(para("RealBStartDate2")) Then
                strSql += String.Format(" and cci.ExpectMaterialDate >= to_date('{0}','YYYY-MM-DD') ", para("RealBStartDate2"))
            End If
        End If

        If para.ContainsKey("RealEStartDate2") Then
            If Not String.IsNullOrWhiteSpace(para("RealEStartDate2")) Then
                strSql += String.Format(" and cci.ExpectMaterialDate <= to_date('{0}','YYYY-MM-DD') ", para("RealEStartDate2"))
            End If
        End If

        If para.ContainsKey("WorkflowStepName") Then
            If Not String.IsNullOrWhiteSpace(para("WorkflowStepName")) Then
                strSql += String.Format(" and cci.WORKFLOWSTEPNAME like '%{0}%' ", para("WorkflowStepName"))
            End If
        End If

        '中心（工区）
        If para.ContainsKey("workcenterid") Then
            If Not String.IsNullOrWhiteSpace(para("workcenterid")) Then
                strSql += String.Format(" and wct.workcenterid = '{0}' ", para("workcenterid"))
            End If
        End If

        '单元（班组）
        If para.ContainsKey("teamid") Then
            If Not String.IsNullOrWhiteSpace(para("teamid")) Then
                strSql += String.Format(" and tm.teamid = '{0}' ", para("teamid"))
            End If
        End If

        '排序
        strSql += " order by ApsSequence asc, pc.prioritycodename "
        Return strSql
    End Function

    ''' <summary>
    ''' 有限产能排产，数据查询
    ''' add:Wangjh 20220424
    ''' </summary>
    ''' <param name="para"></param>
    ''' <param name="isPage">是否分页</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function GetApsPoolContainerInfo(ByVal para As Dictionary(Of String, String), Optional ByVal isPage As Boolean = False) As uMESProcessResult

        Dim strsql As String = GetApsPoolContainerSql(para)
        Dim result As New uMESProcessResult

        If isPage Then '分页
            Dim paraPage As New uMESPagingDataDTO
            paraPage.CurrentPageIndex = Convert.ToInt32(para("CurrentPageIndex"))
            paraPage.PageSize = Convert.ToInt32(para("PageSize").ToString)
            paraPage.strSQL = strsql
            result = DatabaseHelper.RunProcedureForPage(paraPage)
        Else '不分页
            result.Result = True
            result.DBTable = DatabaseHelper.Query_ReturnDatTable(strsql)
        End If

        '批次的实际开始时间REALSTARTDATE：到料时间>计划开始时间=到料时间,到料时间为空：预计到料或齐套时间>计划开始时间=预计到料或齐套时间
        '如果已经在制了，开始时间则是当前时间
        For Each row As DataRow In result.DBTable.Rows
            If String.IsNullOrWhiteSpace(row("MATERIALDATE").ToString) Then
                If Not String.IsNullOrWhiteSpace(row("ExpectMaterialDate").ToString) Then '预计到料或齐套时间
                    row("REALSTARTDATE") = row("ExpectMaterialDate")
                End If
            Else
                If Not String.IsNullOrWhiteSpace(row("MFGSTARTDATE").ToString()) Then
                    If Date.Parse(row("MATERIALDATE").ToString).Date > Date.Parse(row("MFGSTARTDATE").ToString).Date Then
                        row("REALSTARTDATE") = row("MATERIALDATE")
                    End If
                End If
            End If

        Next

        result.DBTable.AcceptChanges()

        Return result
    End Function

    ''' <summary>
    ''' 获取批次的排产信息
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetContainerLMAps(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select cci.mfgordername,cci.containername,cci.productdescription productdesc,cci.qty,wb.workflowname||':'||w.workflowrevision workflowinfo,ai.apstype,
TO_CHAR(ai.starttime,'YYYY-MM-DD HH24:MI:SS') starttime,TO_CHAR(ai.endtime,'YYYY-MM-DD HH24:MI:SS') endtime,w.workflowid,cci.containerid,cci.containertype,cci.productid,ai.apsplaninfoid,
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') MFGENDDDATE,to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate,to_char(cci.materialdate,'yyyy-mm-dd hh24:mi:ss') materialdate, 
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') REALSTARTDATE,cci.workflowstepname,DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,
cci.productname,cci.productrevision,wb.workflowname,w.workflowrevision,cci.togetherflag  from ApsPlanInfo ai
left join containercurrentinfo cci on cci.containerid=ai.containerid
LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
left join workflow w on w.workflowid=ai.workflowid
left join workflowbase wb on wb.workflowbaseid=w.workflowbaseid
where ai.apstype2=2  and ai.isuse=0 </string>.Value '

        If para.ContainsKey("ApsType") Then
            If Not String.IsNullOrWhiteSpace(para("ApsType")) Then
                strSql += String.Format(" and ai.apstype='{0}' ", para("ApsType"))
            End If
        End If

        Dim condition As String = ""
        For Each r In conDt.Rows
            condition += " (1=1 "
            If conDt.Columns.Contains("containerid") And conDt.Columns.Contains("workflowid") Then
                condition += " and ai.containerid='" + r("containerid").ToString() + "'"
                condition += " and ai.workflowid='" + r("workflowid").ToString() + "'"
            End If
            If conDt.Columns.Contains("ApsType") Then
                condition += " and ai.ApsType=" + r("ApsType").ToString()
            End If
            condition += ") or"
        Next
        If condition <> "" Then
            condition = condition.Trim("r", "o")
            strSql += " and ( " + condition + " ) "
        End If

        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '批次的实际开始时间REALSTARTDATE：到料时间>计划开始时间=到料时间,到料时间为空：预计到料或齐套时间>计划开始时间=预计到料或齐套时间
        '如果已经在制了，开始时间则是当前时间
        For Each row As DataRow In dt.Rows
            If String.IsNullOrWhiteSpace(row("MATERIALDATE").ToString) Then
                If Not String.IsNullOrWhiteSpace(row("ExpectMaterialDate").ToString) Then '预计到料或齐套时间
                    row("REALSTARTDATE") = row("ExpectMaterialDate")
                End If
            Else
                If Not String.IsNullOrWhiteSpace(row("MFGSTARTDATE").ToString) Then
                    If Date.Parse(row("MATERIALDATE").ToString).Date > Date.Parse(row("MFGSTARTDATE").ToString).Date Then
                        row("REALSTARTDATE") = row("MATERIALDATE")
                    End If
                End If
            End If
        Next
        dt.AcceptChanges()
        Return dt
    End Function

    ''' <summary>
    ''' 获取工序级的排产信息,甘特图
    ''' yuxj20230413 甘特图只显示当前工序及其后面的工序
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetStepLMAps(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string> select ai.ApsType,ai.containerid,ai.apsplaninfoid,cci.containername,ai.starttime constarttime,ai.endtime conendtime,ai.workflowid,ad.apsplandetailid,ad.workflowstepid,ad.workflowstepname,
  ad.starttime stepstarttime,ad.endtime stependtime,cci.workflowstepid cciworkflowstepid from ApsPlanInfo ai 
  left join containercurrentinfo cci on cci.containerid=ai.containerid
  left join ApsPlanDetail ad on ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
  left join workflowstep wfs on wfs.workflowstepid=ad.workflowstepid
  where 1=1 and ai.isuse=0 and ai.ApsType2=2 </string>.Value
        'strSql = strSql + " and wfs.sequence>=cci.workflowstepsequence "

        If para.ContainsKey("ApsPlanDetailID") Then
            strSql += String.Format(" and ad.ApsPlanDetailID='{0}' ", para("ApsPlanDetailID"))
        End If
        If para.ContainsKey("ApsPlanInfoID") Then
            strSql += String.Format(" and ad.ApsPlanInfoID='{0}' ", para("ApsPlanInfoID"))
        End If

        If para.ContainsKey("ApsPlanInfoIDs") Then
            strSql += String.Format(" and ad.ApsPlanInfoID in ({0}) ", para("ApsPlanInfoIDs"))
        End If

        If para.ContainsKey("ContainerID") Then
            strSql += String.Format(" and ad.ContainerID='{0}' ", para("ContainerID"))
        End If

        If para.ContainsKey("WorkflowStepID") Then
            strSql += String.Format(" and ad.WorkflowStepID='{0}' ", para("WorkflowStepID"))
        End If

        strSql += " order by ai.containerid,ai.workflowid,ad.workflowstepid "

        Return DatabaseHelper.Query_ReturnDatTable(strSql)

    End Function

    ''' <summary>
    ''' 排产记录的人员工作时间段
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetApsEmpTime(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select ae.apsplandetailid,e.fullname,aet.apsplandetailempid,aet.apsplandetailemptimeid,to_char(aet.starttime,'yyyy-mm-dd hh24:mi:ss') starttime
,to_char(aet.endtime,'yyyy-mm-dd hh24:mi:ss') endtime from ApsPlanDetailEmpTime aet
left join employee e on e.employeeid=aet.employeeid
left join apsplandetailemp ae on ae.apsplandetailempid=aet.apsplandetailempid
where 1=1 </string>.Value
        If para.ContainsKey("ApsplanDetailID") Then
            If Not String.IsNullOrWhiteSpace(para("ApsplanDetailID")) Then
                strSql += String.Format(" and ae.apsplandetailid='{0}' ", para("ApsplanDetailID"))
            End If
        End If

        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 获取分厂下有该登录人(计划员)的排产池列表
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetPoolList(ByVal para As System.Collections.Generic.Dictionary(Of String, String)) As System.Data.DataTable
        GetPoolList = New DataTable
        Dim strSql As New StringBuilder
        strSql.AppendLine("  select distinct ap.apspoolid,ap.apspoolname from ApsPool ap  ")
        strSql.AppendLine("  LEFT JOIN apspooljhyemp ape on ape.apspoolid=ap.apspoolid   ")
        strSql.AppendFormat("  where ap.isuse= 0 and ap.factoryid='{0}'  ", para("FactoryID"))
        If para.ContainsKey("EmployeeID") Then
            strSql.AppendFormat("  and ape.EmployeeID='{0}'  ", para("EmployeeID"))
        End If
        strSql.AppendLine(" group by ap.apspoolid,ap.apspoolname ")
        GetPoolList = DatabaseHelper.Query_ReturnDatTable(strSql.ToString())
    End Function

#Region "计算批次排产优先级，计算批次排产期量，调整批次排产顺序号 及检查排产顺序号是否存在 方法"
    ''' <summary>
    ''' 保存调整的排产顺序号
    ''' </summary>
    ''' <param name="para"></param>
    ''' <remarks></remarks>
    Public Function saveSingleSequence(ByVal para As Dictionary(Of String, String)) As Integer
        Dim sql = "update containercurrentinfo cci set cci.ApsSequence='{0}' where cci.containerid='{1}' " '执行更新操作
        sql = String.Format(sql, para("Seq"), para("containerID"))
        saveSingleSequence = DatabaseHelper.ExecuteSql(sql)
    End Function

    ''' <summary>
    ''' 判断输入的顺序号是否存在
    ''' </summary>
    ''' <param name="para"></param>
    ''' <remarks></remarks>
    Public Function checkSingleSequence(ByVal para As Dictionary(Of String, String)) As Boolean
        Dim sql = "select *from containercurrentinfo cci where cci.ApsSequence='{0}' and cci.apspoolid='{1}' "
        sql = String.Format(sql, para("Seq"), para("poolid"))
        Dim dt As DataTable = DatabaseHelper.Query_ReturnDatTable(sql)
        If dt.Rows.Count > 0 Then
            Return False
        Else
            Return True
        End If
    End Function


    ''' <summary>
    ''' 计算排产池期量yuxj20230219
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <remarks></remarks>
    Public Function CalcPoolStepDuration(ByVal conDt As DataTable) As String
        CalcPoolStepDuration = ""
        Dim tempContainername As String = ""
        Try
            '工序信息
            Dim stepSql = <string>SELECT nvl(ws.shipcycle,0) shipcycle,nvl(ws.transfercycle,0) transfercycle,nvl(ws.makecycle,0) makecycle,
                        nvl(ws.preparecycle,0) preparecycle,nvl(ws.commicycle,0) commicycle,ws.workflowstepname,ws.workflowstepid,s.specid,ws.workflowid,
                        decode(s.factoryid,w.factoryid,0,1) isship,s.factoryid specfactoryid,nvl(ws.controlcode,s.controlcode) controlcode,NVL(s.lastcheck,0) lastcheck,
                        nvl(ws.MakeEmpCounts,0) MakeEmpCounts,ws.workcenterid,ws.teamid,nvl(ws.chargingqty,0) chargingqty,u.attributevalue,nvl(ws.multimachineparallel,'0') multimachineparallel
                          from workflow w
                        left join workflowstep ws on ws.workflowid=w.workflowid
                        left join specbase sb on sb.specbaseid=ws.specbaseid
                        left join spec s on s.specid=nvl(sb.revofrcdid,ws.specid)
                        left join userattribute u on u.parentid=w.factoryid and u.userattributename='分厂代码'
                        where w.workflowid='{0}' </string>.Value

            '批次基本信息
            Dim conBaseSql = <string>select cci.containerid,cci.containername,cci.qty,fa.makecalendarid,cci.togetherflag,cci.containertype,
                        round((cci.plannedcompletiondate-sysdate)*24*60) leftMins
                        from containercurrentinfo cci
                        left join factory fa on fa.factoryid=cci.originalfactoryid
                        where cci.containerid='{0}' </string>.Value

            '工序级排产信息sql
            Dim Sql_StepApsSelect = <string>select * from ApsPlanDetail ad where ad.ContainerID='{0}' and ad.isuse=0 </string>.Value

            '工序关联的设备信息
            Dim specResource = <string> select sc.specid,rd.resourceid,rd.resourcename,rd.description,rd.makecalendarid,rd.workshiftid,rd.prioritynum,rd.isbottleneck from specresourceinfo sc 
                                        left join resourcedef rd on rd.resourceid=sc.resourceid where sc.specid='{0}' order by rd.prioritynum desc </string>.Value

            Dim sqlList As New ArrayList
            Dim sql = "update containercurrentinfo cci set cci.StepDuration='{0}',CriticalValue='{1}' where cci.containerid='{2}' " '执行更新操作
            For Each r In conDt.Rows '循环批次
                Dim conBaseDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(conBaseSql, r("containerid").ToString)) '批次基本信息
                Dim conBaseRow = conBaseDt.Rows(0)

                tempContainername = r("containername").ToString
                Dim stepDt As New DataTable '工序列表
                If conDt.Columns.Contains("BEGINSEQUENCE") Then
                    stepDt = DatabaseHelper.Query_ReturnDatTable(String.Concat(String.Format(stepSql, r("workflowid").ToString),
                                                                String.Format(" and ws.Sequence>{0} ", r("BEGINSEQUENCE").ToString),
                                                                " order by ws.workflowstepid ")) '工序
                Else
                    stepDt = DatabaseHelper.Query_ReturnDatTable(String.Format(stepSql, r("workflowid").ToString) + " order by ws.workflowstepid ") '工序
                End If

                Dim leftMins As Single = Convert.ToSingle(conBaseRow("leftMins").ToString) '剩余未完工序的分钟数
                Dim stepCycle As Single = 0 '工序加工周期，单位分钟
                Dim stepCyCle2 As Single = 0 '设备加工周期，单位分钟
                Dim stepCycleSum As Single = 0 '工序期量和

                Dim stepCycleSumLeft As Single = 0 '未完工工序期量和
                Dim CriticalValue As String = 0 '临界比

                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Rows.Add("123") '此处只算工序的加工周期，设备随意赋值 add:Wangjh 20230831

                stepDt.Columns.Add("stepcycle") '增加一列用于存放每个序的加工周期 add:Wangjh 20230831

                Dim stepReourceDt As New DataTable
                For Each stepRow As DataRow In stepDt.Rows
                    resourceTable.Rows(0)("qty") = conBaseRow("qty")
                    '计算工序加工周期
                    GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)
                    stepRow("stepcycle") = resourceTable.Rows(0)("stepcycle")

                    stepCycle = Single.Parse(stepRow("stepcycle")) '工序加工周期，单位分钟 
                    stepCycleSum = stepCycleSum + stepCycle
                Next
                Dim stepDtLeft As New DataTable '未完工工序列表
                '排产信息
                Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, r("containerid").ToString)) '批次工序的计划信息
                stepDtLeft = FilterApsStep(stepDt, stepApsDt, r)
                For Each stepRow As DataRow In stepDtLeft.Rows
                    stepCycle = Single.Parse(stepDt.Select("workflowstepid='" + stepRow("workflowstepid").ToString() + "'")(0)("stepcycle")) '工序加工周期，单位分钟 
                    stepCycleSumLeft = stepCycleSumLeft + stepCycle '未完工工序期量和
                Next

                If stepCycleSumLeft = 0 Then '没有设置期量 没有待加工工序
                    CriticalValue = 0
                ElseIf leftMins < 0 Then '已经超期
                    CriticalValue = 1
                Else
                    CriticalValue = (Math.Round(leftMins / stepCycleSumLeft, 4)).ToString()
                End If

                sqlList.Add(String.Format(sql, stepCycleSum, CriticalValue, r("containerid").ToString))
                If sqlList.Count > 0 Then
                    'r("StepDuration") = stepCycleSum
                    'r("CriticalValue") = CriticalValue
                    DatabaseHelper.ExecuteSqlTran(sqlList)
                    sqlList.Clear()
                End If
            Next
        Catch ex As Exception
            Return tempContainername + " 批次期量异常:" + ex.Message
        End Try
    End Function

    ''' <summary>
    ''' 根据排产池策略计算排产池中的批次的排产优先顺序号 add:Yuxj20230221
    ''' 计算期量，计算排产先后顺序号
    ''' 临界比(订单的批次剩余时间/剩余未加工工序需求时间h(asp))
    ''' 填充进排产池id
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <remarks></remarks>
    Public Function calcContainerApsPriority(ByVal conDt As DataTable) As String
        calcContainerApsPriority = ""
        If conDt.Rows.Count = 0 Then
            Return "没有选择批次" '没有批次
        End If
        '获取排产池及排产策略信息
        Dim apsPoolId As String = conDt.Rows(0)("apspoolid").ToString
        If apsPoolId = String.Empty Then
            Return "没有排产池"
        End If

        Dim strsql As String =
            String.Format("select prioritycode,priorityname,sequence from apspoolpriority apr where apr.apspoolid='{0}' order by apr.sequence asc", apsPoolId)
        Dim dtApsPool As DataTable = DatabaseHelper.Query_ReturnDatTable(strsql)
        If dtApsPool.Rows.Count = 0 Then
            Return "没有排产策略" '没有排产策略
        End If

        '清空旧的排产记录
        strsql = String.Format("update containercurrentinfo cci set cci.apspoolid=null,cci.CriticalValue=null,cci.StepDuration=null,cci.apssequence=null where apspoolid='{0}'", apsPoolId)
        DatabaseHelper.ExecuteSql(strsql)

        If dtApsPool.Select("prioritycode in (3,4,6)").Length > 0 Then
            '3=加工时间长优先 4=加工时间短优先 
            '6=临界比小优先：订单的批次剩余时间/剩余未加工工序需求时间h
            '此方法执行了更新 批次工序期量和临界比
            Dim msg As String = CalcPoolStepDuration(conDt)
            If msg <> "" Then
                Return msg
            End If
        End If

        Dim dtContainer As DataTable =
            conDt.Copy.DefaultView.ToTable(True, "containerid", "apspoolid",
                  "workflowid", "prioritycodename", "ENDTIME", "CriticalValue", "StepDuration", "ApsSequence")
        If Not dtContainer.Columns.Contains("isbottleneck") Then
            dtContainer.Columns.Add("isbottleneck")
        End If

        If dtApsPool.Select("prioritycode=5").Length > 0 Then
            '工序关联的设备信息
            Dim specResource As String =
                <string> 
                  select 1
                  from workflow w
                  left join workflowstep ws
                    on ws.workflowid = w.workflowid
                  left join specbase sb
                    on sb.specbaseid = ws.specbaseid
                  left join specresourceinfo sc
                    on sc.specid = nvl(sb.revofrcdid, ws.specid)
                  left join resourcedef rd
                    on rd.resourceid = sc.resourceid
                 where w.workflowid = '{0}'
                   and rd.isbottleneck = '1'
                </string>

            '5=涉及瓶颈设备优先,工艺期量中是否包含瓶颈设备
            For Each dr In dtContainer.Rows
                Dim dtResource As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(specResource, dr("workflowid")))
                If dtResource.Rows.Count > 0 Then
                    dr("isbottleneck") = "1"
                End If
            Next
        End If

        Dim dvContainer As DataView = dtContainer.DefaultView
        Dim strFilter As String = String.Empty
        For Each dr In dtApsPool.Rows
            If dr("prioritycode").ToString = "1" Then
                strFilter = strFilter + "prioritycodename asc,"
            End If

            If dr("prioritycode").ToString = "2" Then
                strFilter = strFilter + "endtime asc,"
            End If

            'StepDuration在数据表中为number,所以可以直接排序
            If dr("prioritycode").ToString = "3" Then
                strFilter = strFilter + "StepDuration asc,"
            End If

            If dr("prioritycode").ToString = "4" Then
                strFilter = strFilter + "StepDuration desc,"
            End If

            If dr("prioritycode").ToString = "5" Then
                strFilter = strFilter + "CriticalValue asc,"
            End If

            If dr("prioritycode").ToString = "6" Then
                strFilter = strFilter + "isbottleneck desc,"
            End If
        Next

        strFilter = strFilter.Substring(0, strFilter.Length - 1)
        dvContainer.Sort = strFilter
        dtContainer = dvContainer.ToTable()

        Dim sqlList As New ArrayList
        Dim sql As String =
            <string>
                update containercurrentinfo cci 
                set apspoolid='{0}',ApsSequence='{1}'
                where cci.containerid='{2}'  
            </string>
        '执行更新操作
        Dim seq As Integer = 0
        For i As Integer = 0 To dtContainer.Rows.Count - 1
            seq = (i + 1) * 5 '顺序号
            dtContainer.Rows(i)("ApsSequence") = seq.ToString
            Dim dr As DataRow = dtContainer.Rows(i)
            sqlList.Add(String.Format(sql, dr("apspoolid").ToString, seq.ToString, dr("containerid").ToString))
            If sqlList.Count > 999 Then '每一千个提交一次
                DatabaseHelper.ExecuteSqlTran(sqlList)
                sqlList.Clear()
            End If
        Next
        '执行更新
        If sqlList.Count > 0 Then
            DatabaseHelper.ExecuteSqlTran(sqlList)
        End If
    End Function

#End Region

#Region "有限排产，grid查看及调整"

    ''' <summary>
    ''' 获取工序级的排产信息
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetStepLMAps2(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select  ai.ApsType,ai.containerid,ai.apsplaninfoid,cci.mfgordername,cci.containername,to_char(ai.starttime,'yyyy-mm-dd hh24:mi:ss') constarttime,to_char(ai.endtime,'yyyy-mm-dd hh24:mi:ss') conendtime,ai.workflowid,ad.apsplandetailid,ad.workflowstepid,ad.workflowstepname,
  to_char(ad.starttime,'yyyy-mm-dd hh24:mi:ss') stepstarttime,to_char(ad.endtime,'yyyy-mm-dd hh24:mi:ss') stependtime,nvl(sb.revofrcdid,ws.specid) specid,
  decode(re.resourcename,null,'',re.description||'('||re.resourcename||')') RESNAME,wc.workcentername,t.teamname,re.resourceid,wc.workcenterid,t.teamid,
  WS.MAKEEMPCOUNTS,WS.PREPARECYCLE,WS.MAKECYCLE,WS.TRANSFERCYCLE,WS.SHIPCYCLE,WS.COMMICYCLE,wm_concat(e.employeeid) employeeids,ws.sequence,to_char(ad.ResStartTime,'yyyy-mm-dd hh24:mi:ss') ResStartTime,
to_char(ad.ResEndTime,'yyyy-mm-dd hh24:mi:ss') ResEndTime,ad.qty from ApsPlanInfo ai 
  left join containercurrentinfo cci on cci.containerid=ai.containerid
  left join ApsPlanDetail ad on ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
left join workflowstep ws on ws.workflowstepid=ad.workflowstepid
left join specbase sb on sb.specbaseid=ws.specbaseid
left join resourcedef re on re.resourceid=ad.resourceid
left join workcenter wc on wc.workcenterid=ad.workcenterid
left join team t on t.teamid=ad.teamid
left join ApsPlanDetailEmp ae on ae.apsplandetailid=ad.apsplandetailid
left join employee e on e.employeeid=ae.employeeid
  where 1=1 and ai.isuse=0 and ai.ApsType2=2 </string>.Value
        If para.ContainsKey("ApsPlanDetailID") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanDetailID")) Then
                strSql += String.Format(" and ad.ApsPlanDetailID='{0}' ", para("ApsPlanDetailID"))
            End If
        End If
        If para.ContainsKey("ApsPlanInfoID") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanInfoID")) Then
                strSql += String.Format(" and ad.ApsPlanInfoID='{0}' ", para("ApsPlanInfoID"))
            End If
        End If

        If para.ContainsKey("ApsPlanInfoIDs") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanInfoIDs")) Then
                strSql += String.Format(" and ad.ApsPlanInfoID in ({0}) ", para("ApsPlanInfoIDs"))
            End If
        End If

        If para.ContainsKey("ContainerID") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerID")) Then
                strSql += String.Format(" and ad.ContainerID='{0}' ", para("ContainerID"))
            End If
        End If

        If para.ContainsKey("WorkflowStepID") Then
            If Not String.IsNullOrWhiteSpace(para("WorkflowStepID")) Then
                strSql += String.Format(" and ad.WorkflowStepID='{0}' ", para("WorkflowStepID"))
            End If
        End If

        If para.ContainsKey("beginSequence") Then
            If Not String.IsNullOrWhiteSpace(para("beginSequence")) Then
                strSql += String.Format(" and ws.Sequence>={0} ", para("beginSequence"))
            End If
        End If

        If para.ContainsKey("EndSequence") Then
            If Not String.IsNullOrWhiteSpace(para("EndSequence")) Then
                strSql += String.Format(" and ws.Sequence<={0} ", para("EndSequence"))
            End If
        End If

        If para.ContainsKey("ResourceID") Then
            If Not String.IsNullOrWhiteSpace(para("ResourceID")) Then
                strSql += String.Format(" and re.resourceid='{0}' ", para("ResourceID"))
            End If
        End If

        If para.ContainsKey("ContainDate") Then '包含的日期  
            If Not String.IsNullOrWhiteSpace(para("ContainDate")) Then '举例：包含2022/6/26: 开始日期小于<2022/6/26 23:59:59 且结束日期大于2022/6/26 0:0:0 就算
                strSql += String.Format(" and ad.starttime-1<to_date('{0}','yyyy/mm/dd hh24:mi:ss') and ad.endtime>=to_date('{0}','yyyy/mm/dd hh24:mi:ss') ", para("ContainDate"))
            End If
        End If

        If para.ContainsKey("StartDate") Then
            If Not String.IsNullOrWhiteSpace(para("StartDate")) Then
                strSql += String.Format(" and ad.starttime>=to_date('{0}','yyyy/mm/dd hh24:mi:ss') ", para("StartDate"))
            End If
        End If

        If para.ContainsKey("EndDate") Then
            If Not String.IsNullOrWhiteSpace(para("EndDate")) Then
                strSql += String.Format(" and ad.endtime<=to_date('{0}','yyyy/mm/dd hh24:mi:ss') ", para("EndDate"))
            End If
        End If

        If para.ContainsKey("Status") Then
            If Not String.IsNullOrWhiteSpace(para("Status")) Then
                strSql += String.Format(" and ad.status={0} ", para("Status"))
            End If
        End If

        strSql += <string> group by ai.ApsType,ai.containerid,ai.apsplaninfoid,cci.mfgordername,cci.containername,ai.starttime,ad.endtime,ai.workflowid,ad.apsplandetailid,ad.workflowstepid,ad.workflowstepname,
  ad.starttime,ai.endtime,sb.revofrcdid,ws.specid,re.resourcename,re.description,wc.workcentername,t.teamname,re.resourceid,wc.workcenterid,t.teamid,ad.apsplandetailid,
  WS.MAKEEMPCOUNTS,WS.PREPARECYCLE,WS.MAKECYCLE,WS.TRANSFERCYCLE,WS.SHIPCYCLE,WS.COMMICYCLE,ws.sequence,ad.ResStartTime,ad.ResEndTime,ad.qty </string>.Value

        strSql += " order by ai.containerid,ai.workflowid,ad.workflowstepid,ai.ApsType "

        Return DatabaseHelper.Query_ReturnDatTable(strSql)

    End Function

#End Region

#Region "有限产能，正排"

    ''' <summary>
    ''' 创建本次排产的临时表
    ''' add:wangjh 20240830
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub CreateApsTempTable(ByVal apsTempTable As String)
        DatabaseHelper.ExecuteSql("create table " + apsTempTable + " as select * from apsplandetail where 1=2")
        DatabaseHelper.ExecuteSql("CREATE INDEX " + apsTempTable + "_1 ON " + apsTempTable + "(Resstarttime)")
        DatabaseHelper.ExecuteSql("CREATE INDEX " + apsTempTable + "_2 ON " + apsTempTable + "(resourceid)")
    End Sub

    ''' <summary>
    ''' 有限产能，正排
    ''' 日历：分厂日历>设备日历>人员日历 --颗粒度变细
    ''' 班次：设备班次>人员班次 --颗粒度变细
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="ApsOccupyConIdList">考虑占用的批次</param>
    ''' <remarks></remarks>
    Public Overrides Function LMPositiveCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False,
                                                  Optional ByVal ApsOccupyConIdList As List(Of String) = Nothing, Optional ByVal apsTempTable As String = "") As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim _apsTempTable = ""

        Dim isDropTemp = True '是否应该删除临时表
        If Not String.IsNullOrWhiteSpace(apsTempTable) Then '如果是外部传参进来的，不删临时表 
            _apsTempTable = apsTempTable
            isDropTemp = False
        Else
            If conDt.Rows.Count > 1 Then
                _apsTempTable = "apstemp" + Date.Now.ToString("yyyyMMddHHmmssfffffff")
                '创建临时表
                CreateApsTempTable(_apsTempTable)
            End If
        End If

        Dim apsType = "1"
        If IsBottleneck Then
            apsType = "3"
        End If

        Dim alreadyApsConIdList As New List(Of String)  '本排产池本次已经排产的批次id列表
        If ApsOccupyConIdList IsNot Nothing Then
            alreadyApsConIdList = ApsOccupyConIdList
        End If
        For Each r As DataRow In conDt.Rows '循环批次

            Dim stopwatch As Stopwatch = New Stopwatch() '耗时统计
            Dim lastTime = Date.Now
            stopwatch.Start()
            Dim hsMsg = "" '耗时统计-消息
            hsMsg += "开始计算:" + Date.Now.ToString("yyyy-MM-dd HH:mm:ss_fffffff") + ","
            Dim stepHsMsg = "" '耗时统计-每个工序
            Dim lastTime2 = Date.Now


            result.Message = ""
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            If conBaseDt.Rows.Count = 0 Then
                Continue For
            End If
            Dim conBaseRow = conBaseDt.Rows(0)

            Dim apsPoolDt As New DataTable '排产池信息
            If conDt.Columns.Contains("apspoolid") Then
                apsPoolDt = Aps_GetApsPoolDt(r("apspoolid").ToString)
            Else
                apsPoolDt = Aps_GetApsPoolDt(conBaseRow("apspoolid").ToString)
            End If
            '排产截止日期
            Dim deadlineDate As Date = New Date(9999, 1, 1)
            If apsPoolDt.Rows.Count > 0 Then
                If Not apsPoolDt.Rows(0).IsNull("OfficialCycle") Then
                    deadlineDate = Date.Now.Date.AddDays(Val(apsPoolDt.Rows(0)("OfficialCycle")) + 1)
                End If
            End If


            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim startTime = Date.Parse(r("STARTTIME"))
            'startTime = startTime.AddDays(GetDateOffset(1, startTime, faMakeCalendarDt, 0)) '检查工作日历

            Dim stepDt As New DataTable
            If conDt.Columns.Contains("BEGINSEQUENCE") Then
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString, Val(r("BEGINSEQUENCE").ToString))
            Else
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString)  '工序
            End If

            If Not ValidateStepCycle(stepDt) Then '工序维护的时间验证
                result.Message = "批次" + r("containername").ToString + "工序排产期量维护错误！"
                'Continue For
                GoTo conEnd
            End If

            '已排产信息
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            Dim orgStepDt = stepDt.Copy
            stepDt = FilterApsStep(orgStepDt, stepApsDt, r)

            '二次筛选后，不参与的序，清除未下发的排产记录
            Dim clearStepId As New List(Of String)
            'For Each orgStepRow As DataRow In orgStepDt.Rows
            '    If stepDt.Select("workflowstepid='" + orgStepRow("workflowstepid").ToString + "'").Length = 0 Then
            '        clearStepId.Add(orgStepRow("workflowstepid").ToString)
            '    End If
            'Next

            If clearStepId.Count > 0 Then
                DeleteApsPlanData(r("containerid").ToString, "'" + String.Join(",", clearStepId).Replace(",", "','") + "'", "1")
            End If


            If IsBottleneck Then
                If stepDt.Rows.Count = 0 Then
                    result.Result = True
                    GoTo conEnd
                End If
            Else
                If stepDt.Rows.Count = 0 Then
                    result.Message = "批次" + r("containername").ToString + "未找到可排产的工序"
                    'Return result
                    'Continue For '执行后面的批次yuxj20230307
                    GoTo conEnd
                End If
            End If

            Dim curTime = startTime
            '真正排产开始时间，以排产池规则运算后得出,若是手动调整后的重新运算，暂不进行此逻辑
            If Not r.Table.Columns.Contains("manualAdjust") Then
                curTime = GetConApsStartTime(curTime, r, conBaseRow, apsPoolDt)
            End If

            Dim stepPlanSqls As New ArrayList

            '增加批执行排产属性
            Try
                m_containerObj.SaveContainerAttr(r("containerid").ToString, "排产状态", "排产中")
            Catch ex As Exception
                result.Message = "批次" + r("containername").ToString + "增加排产属性报错，原因：" + ex.Message
                GoTo conEnd
            End Try

            hsMsg += "开始循环工序计算-耗时:" + ((Date.Now - lastTime).TotalMilliseconds).ToString() + ","
            lastTime = Date.Now

            '统计本批次计算总工序数，总设备数，是否是提前截止 供客户使用查看 add:wangjh 20250626
            Dim returnData As Dictionary(Of String, Object) = New Dictionary(Of String, Object)
            returnData.Add("calStepCount", 0) '计算总工序数
            returnData.Add("calResCount", 0) '计算总设备数
            returnData.Add("isDeadline", False) '是否提前截止

            For Each stepRow As DataRow In stepDt.Rows

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                Dim _startTime = curTime '此工序的初期开始时间               
                Dim apsPreCheckDt = Aps_GetApsPlaninfoPreSpotCheck(r("containerid").ToString, stepRow("workflowstepid").ToString) '预排产未齐套工序数据 add by longb 20250813
                If apsPreCheckDt.Rows.Count > 0 Then
                    If apsPreCheckDt.Rows(0)("allset").ToString.Equals("2") Then '如果工序预排未齐套，则后续工序不参与排产
                        result.Message = String.Format("批次：{0}工序：{1}预排未齐套，后续工序终止排产", r("containername").ToString, stepRow("workflowstepname").ToString) '给出提示
                        Exit For
                    End If

                    If String.IsNullOrWhiteSpace(apsPreCheckDt.Rows(0)("estallsetdate").ToString) Then '如果工序预齐套时间为空，则后续工序不参与排产
                        result.Message = String.Format("批次：{0}工序：{1}预排预齐套时间为空，后续工序终止排产", r("containername").ToString, stepRow("workflowstepname").ToString) '给出提示
                        Exit For
                    Else
                        If _startTime < Date.Parse(apsPreCheckDt.Rows(0)("estallsetdate")) Then '如果工序开始时间<工序预齐套时间，则工序开始时间=工序预齐套时间
                            _startTime = Date.Parse(apsPreCheckDt.Rows(0)("estallsetdate"))
                        End If
                    End If
                Else
                    result.Message = String.Format("批次：{0}工序：{1}预排未做点检，后续工序终止排产", r("containername").ToString, stepRow("workflowstepname").ToString) '给出提示
                    Exit For
                End If
                '_startTime.AddDays(GetDateOffset(1, _startTime, faMakeCalendarDt, 0)) '检查工作日历,开始时间顺移

                If stepRow("isship").ToString = "1" Then '转工序 
                    stepReourceDt.Rows.Clear()
                    resourceTable.Rows.Clear()
                    stepResourceIds = ""
                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 
                    stepReourceDt.Rows.Clear()
                    resourceTable.Rows.Clear()
                    stepResourceIds = ""
                ElseIf stepRow("lastcheck").ToString = "1" Then '检验序 
                    stepReourceDt.Rows.Clear()
                    resourceTable.Rows.Clear()
                    stepResourceIds = ""
                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "批次" + r("containername").ToString + "工序" + stepRow("workflowstepname").ToString + "无设备"
                        'Return result
                        Exit For
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "批次" + r("containername").ToString + "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        'Return result
                        Exit For
                    End If
                End If
                '本次总工序数,总设备数 add:wangjh 20250626
                returnData("calStepCount") = returnData("calStepCount") + 1
                returnData("calResCount") = returnData("calResCount") + resourceTable.Rows.Count

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, alreadyApsConIdList, curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType, _apsTempTable)
                'For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                '    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                '        apsOccupyResDT.Rows.RemoveAt(i)
                '    End If
                'Next
                'apsOccupyResDT.AcceptChanges()

                '此工序设备的所有保养信息
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))

                '此工序设备所有的日历信息
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)

                '此工序设备所有的班次信息
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)
                '设备模拟加工完成

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString '是否多设备并行加工
                If isMachineParallel = "1" Then '并行设备加工下，任何一台设备超过截止日期，此批次终止后续排产
                    If resSimulationDt.AsEnumerable().Any(Function(resSLRow) Date.Parse(resSLRow("simulateStart").ToString()).Date >= deadlineDate) Then
                        '到截止日期后，若后续的工序还有以前的排产数据，则应该清除
                        DeleteApsPlanData(conApsID, Integer.Parse(stepRow("sequence").ToString))
                        result.Message = String.Format("批次：{0}工序：{1}排产超过截止日期，后续工序终止排产", r("containername").ToString, stepRow("workflowstepname").ToString) '给出提示
                        returnData("isDeadline") = True 'add:wangjh 20250626 统计是否是截止
                        Exit For
                        'GoTo exitFor
                    End If
                Else '非行设备加工下，去除超过截止日期的记录，若最终剩余0个设备，此批次终止后续排产
                    Dim resSimulationDt2 = resSimulationDt.AsEnumerable().Where(Function(resSLRow) Date.Parse(resSLRow("simulateStart").ToString()).Date < deadlineDate)
                    If resSimulationDt2.Count = 0 Then
                        '到截止日期后，若后续的工序还有以前的排产数据，则应该清除
                        DeleteApsPlanData(conApsID, Integer.Parse(stepRow("sequence").ToString))
                        result.Message = String.Format("批次：{0}工序：{1}排产超过截止日期，后续工序终止排产", r("containername").ToString, stepRow("workflowstepname").ToString) '给出提示
                        returnData("isDeadline") = True 'add:wangjh 20250626 统计是否是截止
                        Exit For
                        'GoTo exitFor
                    End If
                    resSimulationDt = resSimulationDt2.CopyToDataTable
                End If

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员

                '设备模拟加工数据，分配人员
                Dim recResSimulationDt As New DataTable
                Dim isApsToEmp As Boolean = True '是否排到人
                If para.ContainsKey("IsApsToEmp") Then '是否排到人,1:是,0:是
                    If para("IsApsToEmp") = "0" Then
                        isApsToEmp = False
                    End If
                End If
                If isApsToEmp Then
                    recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, faMakeCalendarDt, stepResMKDt, stepShiftDt, alreadyApsConIdList, apsType, makeEmpCounts, isMachineParallel)
                Else
                    recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, makeEmpCounts, isMachineParallel)
                End If
                '到此此工序模拟加工完成，取出计算后的，工序的加工信息               
                'Dim confirmResSL As DataRow = recResSimulationDt.Rows(0)

                'yuxj30230309多设备并行逻辑处理
                Dim strFilter = "workflowstepid='" + stepRow("workflowstepid").ToString + "'"
                Dim stepApsRows = stepApsDt.Select(strFilter)

                '为保证旧的排产明细id可以使用，此处逻辑改为如果设备个数超过工序记录数，超出的的设备记录调用插入功能
                '如果设备个数小于工序记录数，超出的的工序记录调用删除功能
                Dim delStepApsId As String = ""
                If stepApsRows.Length > recResSimulationDt.Rows.Count Then
                    '超过的Apsplandetailid记录执行删除
                    For i As Integer = recResSimulationDt.Rows.Count - 1 To stepApsRows.Length - 1
                        delStepApsId = stepApsRows(i)("Apsplandetailid").ToString

                        If stepApsRows(i)("Apsplandetailid").ToString() <> "0" Then
                            Continue For
                        End If

                        stepPlanSqls.Add(String.Format(Sql_StepApsDel, delStepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, delStepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, delStepApsId))
                    Next
                End If

                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1 Step 1
                    Dim confirmResSL As DataRow = recResSimulationDt.Rows(i)

                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                    '存入最佳设备和人员到排产计划表                    
                    '生成工序计划sql

                    Dim stepApsId As String = ""
                    If i <= stepApsRows.Length - 1 Then
                        stepApsId = stepApsRows(i)("Apsplandetailid")
                        'If stepRow("isship").ToString = "1" Then '新增对已转出的转工序进行判断 add by zhangrj 20240424
                        '    Dim shipSql As String = String.Format("SELECT apd.endtime FROM apsplandetail apd WHERE apd.apsplandetailid = '{0}' AND nvl(apd.status,'0') != '0' AND nvl(apd.status,'0') != '4' AND apd.endtime IS NOT NULL", stepApsId)
                        '    Dim dtShipOld = DatabaseHelper.Query_ReturnDatTable(shipSql)
                        '    If dtShipOld.Rows.Count > 0 Then '如果转工序已经转出确认了，则不更新 add by zhangrj 20240424
                        '        curTime = Date.Parse(dtShipOld.Rows(0)("endtime").ToString()) '将当前时间更改为转工序旧的的排产结束时间（替换当前转工序计算的时间）
                        '        Continue For
                        '    End If
                        'End If
                        stepPlanSqls.Add(String.Format(Sql_StepApsUpdate, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), stepRow("workcenterid"), stepRow("Teamid"),
                                                 confirmResSL("resourceid"), stepRow("specfactoryid").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty").ToString, conApsID, stepRow("workflowstepid").ToString, stepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, stepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, stepApsId))
                    Else
                        stepApsId = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepApsInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                 confirmResSL("resourceid"), para("EmployeeID"), stepRow("specfactoryid").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty").ToString, stepRow("specid").ToString))
                    End If
                    '临时表存储 add:wangjh 20240830
                    If Not String.IsNullOrWhiteSpace(_apsTempTable) Then
                        stepPlanSqls.Add(String.Format("insert into " + _apsTempTable + Sql_StepApsTempInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                    stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                     confirmResSL("resourceid"), para("EmployeeID"), stepRow("specfactoryid").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty").ToString, stepRow("specid").ToString))

                    End If
                    For Each _emp In confirmResSL("employeeids").ToString.Split(",")
                        If String.IsNullOrWhiteSpace(_emp) Then
                            Continue For
                        End If
                        'apsplandetailempid特别处理
                        Dim apsplandetailempid As String = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsInsert, stepApsId, _emp, apsplandetailempid))

                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsInsert, apsplandetailempid, Guid.NewGuid.ToString(), confirmResSL("simulateStart"), confirmResSL("simulateEnd"), _emp))

                        ''yuxj20220823热表数据 增加apsplandetailemptime的插入，特殊处理，其他车间和设备时间一致
                        'If stepRow("attributevalue").ToString() = "602" Then '会不会改名或多个包含热表字段的数据？
                        '    AddEmployeeTime(stepPlanSqls, _emp, apsplandetailempid, confirmResSL)
                        'Else
                        '    stepPlanSqls.Add(String.Format(Sql_EmpTimeApsInsert, apsplandetailempid, Guid.NewGuid.ToString(), confirmResSL("simulateStart"), confirmResSL("simulateEnd"), _emp))
                        'End If
                    Next

                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next
                result.ReturnData = returnData
                stepHsMsg += "工序：" + stepRow("workflowstepname").ToString + " 计算完成-耗时:" + ((Date.Now - lastTime2).TotalMilliseconds).ToString() + ","
                lastTime2 = Date.Now

            Next

            If Not String.IsNullOrWhiteSpace(stepHsMsg) Then
                m_containerObj.SaveContainerAttr(r("containerid").ToString, "排产工序明细耗时", stepHsMsg)
            End If

            hsMsg += "所有工序计算完成-耗时:" + ((Date.Now - lastTime).TotalMilliseconds).ToString() + ","
            lastTime = Date.Now

            Try
                m_containerObj.SaveContainerAttr(r("containerid").ToString, "排产状态", "排产完")
            Catch ex As Exception
                result.Message = "批次" + r("containername").ToString + "排产计算完成，但更新排产属性报错（可忽视），原因：" + ex.Message
            End Try

            '执行存入工序
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))

            If conApsDt2.Rows.Count > 0 Then
                '生成批次计划sql
                stepPlanSqls.Clear()
                If conApsDt.Rows.Count > 0 Then
                    stepPlanSqls.Add(String.Format(Sql_ConApsUpdate, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), conApsID))
                Else
                    stepPlanSqls.Add(String.Format(Sql_ConApsInsert, conApsID, apsType, r("containerid").ToString, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                                   r("workflowid").ToString))
                End If
                '执行存入批次
                DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            End If

            '存储已经排产的批次id子串，
            If Not alreadyApsConIdList.Contains(r("containerid").ToString) And conBaseRow("containertype").ToString <> "7" Then
                alreadyApsConIdList.Add(r("containerid").ToString)
            End If

            hsMsg += "存入到数据库完成-耗时:" + ((Date.Now - lastTime).TotalMilliseconds).ToString() + ","
            lastTime = Date.Now

            stopwatch.Stop()
            hsMsg += "总耗时：" + stopwatch.ElapsedMilliseconds().ToString()
            m_containerObj.SaveContainerAttr(r("containerid").ToString, "排产耗时", hsMsg)
            m_containerObj.SaveContainerAttr(r("containerid").ToString, "排产总耗时", stopwatch.ElapsedMilliseconds().ToString())

conEnd:
            If Not String.IsNullOrWhiteSpace(result.Message) Then
                m_containerObj.WriteContainerSCLog("", r("containerid").ToString, r("containername").ToString, result.Message, 1, para("EmployeeID"))
            Else
                m_containerObj.DeleteContainerSCLog(r("containerid").ToString, 1)
            End If

        Next

        '临时表删除
        If isDropTemp And Not String.IsNullOrWhiteSpace(_apsTempTable) Then
            DatabaseHelper.ExecuteSql("drop table " + _apsTempTable)
        End If
        result.Result = True
        Return result
    End Function

    ''' <summary>
    ''' 为设备列表增加qty信息
    ''' </summary>
    ''' <param name="isMachineParallel">是否并行加工</param>
    ''' <param name="dtRec">设备列表</param>
    ''' <param name="qty">批次数量</param>
    ''' <remarks></remarks>
    Sub calcResourceAndQty(ByVal isMachineParallel As String, ByRef dtRec As DataTable, ByVal qty As String)
        If Not dtRec.Columns.Contains("qty") Then
            dtRec.Columns.Add("qty")
        End If

        If isMachineParallel = "0" Then
            Dim confirmResSL As DataRow = dtRec.Rows(0)
            dtRec = dtRec.Clone
            dtRec.ImportRow(confirmResSL)
            dtRec.AcceptChanges()
            dtRec.Rows(0)("qty") = qty
        ElseIf isMachineParallel = "1" Then
            '将数量拆分
            Dim containerQty As Integer = Integer.Parse(qty) '批次数量
            Dim imod As Integer = Integer.Parse(qty) Mod dtRec.Rows.Count
            Dim iavg As Integer = Integer.Parse(qty) / dtRec.Rows.Count
            For i As Integer = 0 To dtRec.Rows.Count - 1 Step 1
                If i = dtRec.Rows.Count - 1 Then
                    dtRec.Rows(i)("qty") = iavg + imod
                Else
                    dtRec.Rows(i)("qty") = iavg
                End If
            Next
        End If
    End Sub
    ''' <summary>
    ''' yuxj20220823
    ''' 热表分厂 将每个人的工作时间加入apsplandetailemptime
    ''' </summary>
    ''' <param name="stepPlanSqls">sql列表</param>
    ''' <param name="empid">人员id</param>
    ''' <param name="apsplandetailempid"></param>
    ''' <param name="confirmResSL"></param>
    ''' <remarks></remarks>
    Private Sub AddEmployeeTime(ByRef stepPlanSqls As ArrayList, ByVal empid As String, ByVal apsplandetailempid As String, ByVal confirmResSL As DataRow)
        Dim slstartDate As String = confirmResSL("simulateStart")
        Dim slendDate As String = confirmResSL("simulateEnd")

        Dim startDate_0 As String = confirmResSL("simulateStart") '第一天的开始时间 需要根据班次调整
        Dim endDate_1 As String = confirmResSL("simulateEnd") '最后一天的结束时间 需要根据班次调整
        '人员对应的班次 
        Dim empshift As String =
            <string>
                select e.employeeid,e.workshiftid,ws.startdate,ws.enddate from employee e                
                inner join workshift ws on e.workshiftid=ws.workshiftid and ws.isuse=0  where e.employeeid ='{0}'                 
            </string>
        Dim empShiftDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(empshift, empid))
        Dim starttime As String = "00:00:00" '班次的开始默认
        Dim endtime As String = "23:59:59" '班次的结束默认
        If empShiftDt.Rows.Count > 0 Then
            starttime = empShiftDt.Rows(0)("startdate").ToString
            endtime = empShiftDt.Rows(0)("enddate").ToString

            '根据班次调整设备模拟加工的开始结束时间  
            If Date.Parse(slstartDate) < GetRunStartOrEndDate(Date.Parse(Date.Parse(slstartDate).ToShortDateString), starttime) Then
                startDate_0 = GetRunStartOrEndDate(Date.Parse(Date.Parse(slstartDate).ToShortDateString), starttime).ToString
            End If

            If Date.Parse(slstartDate) > GetRunStartOrEndDate(Date.Parse(Date.Parse(slstartDate).ToShortDateString), endtime) Then
                '模拟开始时间在班次之后 取开始时间后一天的班次开始时间为开始时间
                startDate_0 = GetRunStartOrEndDate(Date.Parse(Date.Parse(slstartDate).AddDays(1).ToShortDateString), starttime)
            End If

            If Date.Parse(slendDate) >= GetRunStartOrEndDate(Date.Parse(Date.Parse(slendDate).ToShortDateString), endtime) Then
                endDate_1 = GetRunStartOrEndDate(Date.Parse(Date.Parse(slendDate).ToShortDateString), endtime).ToString
            End If

            If Date.Parse(slendDate) < GetRunStartOrEndDate(Date.Parse(Date.Parse(slendDate).ToShortDateString), starttime) Then
                '模拟开始时间在班次之前 取结束时间前一天的班次结束时间为结束时间
                endDate_1 = GetRunStartOrEndDate(Date.Parse(Date.Parse(slendDate).AddDays(-1).ToShortDateString), endtime).ToString
            End If
        End If


        Dim insertApsTime As String =
            <string>
                insert into apsplandetailemptime
                    (apsplandetailempid, apsplandetailemptimeid,starttime, isuse, endtime, employeeid)
                  values
                    ('{0}', '{1}', to_date('{2}','yyyy/mm/dd hh24:mi:ss'), '0', to_date('{3}','yyyy/mm/dd hh24:mi:ss'),'{4}')
            </string>
        '日历查询
        Dim empCalend As String =
            <string>
                select e.employeeid,e.makecalendarid,e.workshiftid,mcd.holidaydate from employee e                
                left join makecalendardetail mcd on e.makecalendarid=mcd.makecalendarid and mcd.isuse=0
                where e.employeeid ='{0}' and Holidaydate between trunc(to_date('{1}','yyyy/mm/dd hh24:mi:ss')) and trunc(to_date('{2}','yyyy/mm/dd hh24:mi:ss'))
                order by mcd.holidaydate asc
            </string>
        Dim empCalendDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(empCalend, empid, startDate_0, endDate_1))
        If empCalendDt.Rows.Count = 0 Then
            '没有日历，和设备开始时间，结束时间相同
            stepPlanSqls.Add(String.Format(insertApsTime, apsplandetailempid, System.Guid.NewGuid().ToString(), startDate_0, endDate_1, empid))
        Else
            Dim beginDate As String = empCalendDt.Rows(0)("holidaydate")
            Dim endDate As String = empCalendDt.Rows(empCalendDt.Rows.Count - 1)("holidaydate")

            If Date.Parse(Date.Parse(startDate_0).ToShortDateString) = Date.Parse(beginDate) Then
                '设备模拟开始时间为人员休息日
            Else
                '插入从设备模拟开始到日历开始时间前一天的数据
                Dim d_beginDate_1 As String = GetRunStartOrEndDate(Date.Parse(beginDate).AddDays(-1), endtime)
                stepPlanSqls.Add(String.Format(insertApsTime, apsplandetailempid, System.Guid.NewGuid().ToString(), startDate_0, d_beginDate_1, empid))
            End If

            If Date.Parse(endDate_1) = Date.Parse(endDate) Then
                '设备模拟结束时间为人员休息日
            Else
                '插入从设备模拟日历结束时间后一天 到 模拟加工结束时间 的数据
                Dim d_endDate_1 As String = GetRunStartOrEndDate(Date.Parse(endDate).AddDays(1), starttime)
                stepPlanSqls.Add(String.Format(insertApsTime, apsplandetailempid, System.Guid.NewGuid().ToString(), d_endDate_1, endDate_1, empid))
            End If

            Dim i As Integer = 0
            For Each dr As DataRow In empCalendDt.Rows
                i = empCalendDt.Rows.IndexOf(dr)
                If i = empCalendDt.Rows.Count - 1 Then '最后一行，退出循环
                    Exit For
                End If
                If Date.Parse(dr("holidaydate")).AddDays(1) = Date.Parse(empCalendDt.Rows(i + 1)("holidaydate")) Then
                    '连续的休息日
                    Continue For
                Else
                    '插入从设备模拟开始到日历休息第i条记录开始后一天 到 i + 1记录的前一天 的数据
                    Dim d_beginDate_1 As String = GetRunStartOrEndDate(Date.Parse(dr("holidaydate")).AddDays(1), starttime)
                    Dim d_endDate_1 As String = GetRunStartOrEndDate(Date.Parse(empCalendDt.Rows(i + 1)("holidaydate")).AddDays(-1), endtime)
                    stepPlanSqls.Add(String.Format(insertApsTime, apsplandetailempid, System.Guid.NewGuid().ToString(), d_beginDate_1, d_endDate_1, empid))
                End If
            Next

        End If
    End Sub

    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，EndDate:结束时间
    ''' </summary>
    ''' <param name="simulateStart"></param>
    ''' <param name="simulateEnd"></param>
    ''' <param name="stepResMKDt"></param>
    ''' <param name="resUpKeepDt"></param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle"></param>
    ''' <param name="stepCycle"></param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMAscSLResEndDate(ByVal resourceId As String, ByVal simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                          ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                          ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal isFirst As Boolean)

        simulateEnd = simulateEnd.AddDays(GetDateOffset(1, simulateEnd, stepResMKDt, 0))

        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(1, simulateEnd, stepResMKDt, 1)
                    simulateEnd = simulateEnd.AddDays(offset + 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resRunCycle2, _stepShiftDt, stepResMKDt, resourceId)
        Else '递归进来的情况
            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateEnd, resShiftStart) '设备开始时间
            '------------更新班次最早最晚时间 add by zhangrj 20250923----------------
            Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateEnd) '获取设备在这一天的新的班次明细
            For Each drd As DataRow In dtDayWorkshift.Rows
                Dim rStart = GetRunStartOrEndDate(simulateEnd, drd("starttime")) '开始时间
                Dim rEnd = GetRunStartOrEndDate(simulateEnd, drd("endtime")) '结束时间
                If resStart > rStart Then '如果设备初始开始时间大于该班次的开始时间，则更新设备初始开始时间
                    resStart = rStart
                End If
                If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                    resEnd = rEnd
                End If
            Next
            '----------------------------

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resEnd = resEnd.AddDays(1)
            End If

            If resEnd < simulateEnd Then '例如：工序18：30结束，而设备默认17：30结束，追加一天
                Dim differ = GetDifferMins(simulateEnd, resEnd)

                simulateEnd = resEnd
                simulateEnd = simulateEnd.AddDays(1)
                simulateEnd = simulateEnd.AddDays(GetDateOffset(1, simulateEnd, stepResMKDt, 0))

                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, differ, _stepShiftDt, stepResMKDt, resourceId)
            ElseIf simulateEnd < resStart Then '结束时间，在设备未开始，eg:工序7:30结束，而设备8：30开始----->实际结束：8：30+1h
                Dim differ = GetDifferMins(resStart, simulateEnd)
                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, differ, _stepShiftDt, stepResMKDt, resourceId)
            Else '正常情况，结束时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateEnd = GetResApsStartOrEndDate(1, simulateEnd, _stepShiftDt, dtDayWorkshift)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '{``````[````}`````] 情况下才会只让 结束时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        'Dim removeRowIndex As New List(Of Integer) '需要移除的行
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1
            Dim resUpKeepRow = resUpKeepDt.Rows(i)

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If upkeepStart >= simulateStart And upkeepStart <= simulateEnd Then
                If upkeepEnd <= simulateEnd Then '{`````[````]````}
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf upkeepEnd > simulateEnd Then '{``````[````}`````]
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                Dim _resUpKeepOffset2 = GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)
                resUpKeepOffset2 += _resUpKeepOffset2
                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, _resUpKeepOffset2, _stepShiftDt, stepResMKDt, resourceId)
            End If

        Next

        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？

        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1
            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If apsStartTime >= simulateStart And apsStartTime <= simulateEnd Then
                If apsEndTime <= simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf apsEndTime > simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                Dim _resApsOccupyOffset2 = GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)
                resApsOccupyOffset2 += _resApsOccupyOffset2
                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, _resApsOccupyOffset2, _stepShiftDt, stepResMKDt, resourceId)
            End If
        Next

        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            If isFirst Then
                recursionStartTime = Date.Now
            End If
            If (Date.Now - recursionStartTime).TotalMinutes < 5 Then
                LMAscSLResEndDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, False)
            Else '超过5分钟的递归，判定为死循环，记录日志
                LogHelp.WriteLog("排产正排出现死循环,LimitedCapacityBusiness.LMAscSLResEndDate各参数为：" +
                                 "resourceId：" + resourceId +
                                 "simulateStart：" + simulateStart.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "simulateEnd：" + simulateEnd.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt) +
                                 "resUpKeepDt：" + JsonConvert.SerializeObject(resUpKeepDt) +
                                 "_apsOccupyResDT：" + JsonConvert.SerializeObject(_apsOccupyResDT) +
                                 "_stepShiftDt" + JsonConvert.SerializeObject(_stepShiftDt) +
                                 "resShiftStart：" + resShiftStart +
                                 "resShiftEnd：" + resShiftEnd +
                                 "resCycle：" + resCycle.ToString() +
                                 "stepCycle：" + stepCycle.ToString() +
                                 "isFirst：" + isFirst.ToString()
                                 )
            End If
        End If

    End Sub

    ''' <summary>
    ''' 设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="simulateStart">设备模拟加工开始时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle">设备运转周期(分钟)</param>
    ''' <remarks></remarks>
    Sub LMAscSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String, ByVal resCycle As Single, Optional ByVal isFirst As Boolean = True)

        '计算设备模拟加工开始时间

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateStart = simulateStart.AddDays(GetDateOffset(1, simulateStart, stepResMKDt, 0)) '检查工作日历,开始时间顺移

        '班次因素
        Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备初期开始时间
        Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备初期结束时间
        '------查询新增班次日历的数据，更新班次最早最晚时间 add by zhangrj 20250923------------
        Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateStart) '获取设备在这一天的新的班次明细
        For Each drd As DataRow In dtDayWorkshift.Rows
            Dim rStart = GetRunStartOrEndDate(simulateStart, drd("starttime")) '开始时间
            Dim rEnd = GetRunStartOrEndDate(simulateStart, drd("endtime")) '结束时间
            If resStart > rStart Then '如果设备初始开始时间大于该班次的开始时间，则更新设备初始开始时间
                resStart = rStart
            End If
            If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                resEnd = rEnd
            End If
        Next
        '----------------------------

        'Dim differ = GetDifferMins(simulateStart, resStart)

        If simulateStart <= resStart Then '工序开始7：30 ，设备默认开始8：30
            simulateStart = resStart
        ElseIf resStart < simulateStart And simulateStart < resEnd Then '工序开始时间在设备周期内,工序开始：9：30，而设备默认开始8：30,此时要考虑休息时间段如果9:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateStart = GetResApsStartOrEndDate(1, simulateStart, _stepShiftDt, dtDayWorkshift)
            End If

        Else '此情况例子，工序开始时间：18：30，而设备结束时间17：30，设备开始时间就要追加一天,跨天考虑日历情况
            simulateStart = resStart
            simulateStart = simulateStart.AddDays(1)

        End If
        '日历检查
        simulateStart = simulateStart.AddDays(GetDateOffset(1, simulateStart, stepResMKDt, 0))

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '[`````{```]````} 或者 '[`````{````}```]情况下才会让 开始时间 偏移


        Dim resUpKeepOffset1 = 0 '保养检查后，需要偏移的分钟数，开始时间
        Dim lastUpKeepEndTime = simulateStart '设备最新保养占用的最大时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟开始时间在保养周期内才会偏移
            If Not (lastUpKeepEndTime >= upkeepStart And lastUpKeepEndTime < upkeepEnd) Then
                Continue For
            End If
            '必须大于上次占用最大时间才偏移
            If upkeepEnd <= lastUpKeepEndTime Then
                Continue For
            End If

            resUpKeepOffset1 += GetDifferMins(upkeepEnd, lastUpKeepEndTime)
            lastUpKeepEndTime = upkeepEnd
        Next

        simulateStart = simulateStart.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况
        Dim resApsOccupyOffset1 = 0 '排产占用后，需要偏移的分钟数
        Dim lastOccupyEndTime = simulateStart '设备最新占用的最大时间
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("resstarttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("resendtime").ToString)

            '模拟开始时间在占用周期内才会偏移
            If Not (lastOccupyEndTime >= apsStartTime And lastOccupyEndTime < apsEndTime) Then
                Continue For
            End If
            '必须大于上次占用最大时间才偏移
            If apsEndTime <= lastOccupyEndTime Then
                Continue For
            End If


            resApsOccupyOffset1 += GetDifferMins(apsEndTime, lastOccupyEndTime)
            lastOccupyEndTime = apsEndTime
        Next

        simulateStart = simulateStart.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            '开始递归了，防止死循环 
            If isFirst Then
                recursionStartTime = Date.Now
            End If
            '递归超过5分钟，不递归 
            If (Date.Now - recursionStartTime).TotalMinutes < 5 Then
                LMAscSLResStartDate(resourceId, simulateStart, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, False)
            Else '超过5分钟的递归，判定为死循环，记录日志
                LogHelp.WriteLog("排产正排出现死循环,LimitedCapacityBusiness.LMAscSLResStartDate各参数为" +
                                 "resourceId：" + resourceId +
                                 "simulateStart：" + simulateStart.ToString("yyyy-MM-dd HH:mm:ss") +
                                 "stepResMKDt：" + JsonConvert.SerializeObject(stepResMKDt) +
                                 "resUpKeepDt：" + JsonConvert.SerializeObject(resUpKeepDt) +
                                 "_apsOccupyResDT：" + JsonConvert.SerializeObject(_apsOccupyResDT) +
                                 "_stepShiftDt" + JsonConvert.SerializeObject(_stepShiftDt) +
                                 "resShiftStart：" + resShiftStart +
                                 "resShiftEnd：" + resShiftEnd +
                                 "resCycle：" + resCycle.ToString() +
                                 "isFirst：" + isFirst.ToString()
                                 )
            End If
        End If

    End Sub

    ''' <summary>
    ''' 正排模拟加工,工序各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="startTime">工序初期开始时间</param>
    ''' <param name="endTime">工序初期结束时间</param>     
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMAscSimulationByRes(ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date
                                   ) As DataTable


        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间
        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备分配的数量：多机并行时按数量分配，非多机并行时各设备数量相同

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历

        Dim resultC = New ConcurrentBag(Of DataTable)

        Dim paraOptions = New ParallelOptions()
        paraOptions.MaxDegreeOfParallelism = Environment.ProcessorCount \ 2
        Parallel.For(0, resourceTable.Rows.Count, paraOptions,
                     Sub(i)
                         Dim dr = resourceTable.Rows(i)
                         Dim reourceId As String = dr("resourceid").ToString
                         Dim stepCycle = Single.Parse(dr("stepCycle").ToString)
                         Dim stepCyCle2 = Single.Parse(dr("stepCyCle2").ToString)

                         Dim simulateStart = startTime '模拟开始时间
                         Dim simulateEnd = endTime '模拟结束时间

                         Dim simulateStart2 = startTime '设备模拟开始时间
                         Dim simulateEnd2 = endTime '设备模拟结束时间

                         Dim result2 = result.Clone
                         Dim resultRow As DataRow = result2.NewRow
                         resultRow("resourceid") = reourceId

                         '+设备本身日历，设备上有日历用设备的日历，无则用工厂日历
                         Dim _stepResMKDt = faMakeCalendarDt.Copy
                         If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
                             _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
                         End If

                         '取出此设备的班次加工信息
                         Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
                         Dim _stepShiftDt = stepShiftDt.Clone
                         Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
                         If _stepShiftDrs.Length > 0 Then
                             _stepShiftDt = _stepShiftDrs.CopyToDataTable
                             resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                             resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
                         End If

                         '----计算设备的工作周期(分钟)
                         Dim resCycle = 1439
                         If _stepShiftDt.Rows.Count > 1 Then
                             resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
                         End If

                         '此设备的保养计划
                         Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")
                         Dim _stepResUpkeepDt As New DataTable
                         If resUpKeepRows.Length > 0 Then
                             _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
                         End If

                         '此设备占用数据
                         Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
                         Dim _apsOccupyResDT As New DataTable
                         If resApsOccupyRows.Length > 0 Then
                             _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
                         End If

                         '计算工序模拟加工的开始时间
                         LMAscSLResStartDate(reourceId, simulateStart, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
                         simulateEnd = simulateStart

                         '计算设备模拟加工的开始时间
                         'LMAscSLResStartDate(reourceId, simulateStart2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
                         simulateStart2 = simulateStart
                         simulateEnd2 = simulateStart2
                         '计算设备模拟加工的结束时间 
                         LMAscSLResEndDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt.Copy, _apsOccupyResDT.Copy, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCyCle2, True)

                         '计算工序模拟加工的结束时间 暂时只用加上准备时间得到工序的结束时间
                         'LMAscSLResEndDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt.Copy, _apsOccupyResDT.Copy, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)
                         simulateEnd = simulateEnd2.AddMinutes(stepCycle - stepCyCle2)

                         resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
                         resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

                         resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
                         resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
                         resultRow("qty") = dr("qty").ToString
                         result2.Rows.Add(resultRow)

                         resultC.Add(result2)
                     End Sub)
        For Each dr As DataTable In resultC
            result.Merge(dr)
        Next

        'Dim stepCycle As Single = 0 '工序加工周期
        'Dim stepCyCle2 As Single = 0 '设备加工周期(去除了转运时间)
        'For Each dr As DataRow In resourceTable.Rows
        '    Dim reourceId As String = dr("resourceid").ToString
        '    stepCycle = Single.Parse(dr("stepCycle").ToString)
        '    stepCyCle2 = Single.Parse(dr("stepCyCle2").ToString)

        '    Dim simulateStart = startTime '模拟开始时间
        '    Dim simulateEnd = endTime '模拟结束时间

        '    Dim simulateStart2 = startTime '设备模拟开始时间
        '    Dim simulateEnd2 = endTime '设备模拟结束时间

        '    Dim resultRow As DataRow = result.NewRow
        '    resultRow("resourceid") = reourceId

        '    '+设备本身日历，设备上有日历用设备的日历，无则用工厂日历
        '    Dim _stepResMKDt = faMakeCalendarDt.Copy
        '    If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
        '        _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
        '    End If

        '    '取出此设备的班次加工信息
        '    Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
        '    Dim _stepShiftDt = stepShiftDt.Clone
        '    Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
        '    If _stepShiftDrs.Length > 0 Then
        '        _stepShiftDt = _stepShiftDrs.CopyToDataTable
        '        resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
        '        resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
        '    End If


        '    '----计算设备的工作周期(分钟)
        '    Dim resCycle = 1439
        '    If _stepShiftDt.Rows.Count > 1 Then
        '        resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
        '    End If

        '    '此设备的保养计划
        '    Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")
        '    Dim _stepResUpkeepDt As New DataTable
        '    If resUpKeepRows.Length > 0 Then
        '        _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
        '    End If

        '    '此设备占用数据
        '    Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
        '    Dim _apsOccupyResDT As New DataTable
        '    If resApsOccupyRows.Length > 0 Then
        '        _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
        '    End If

        '    '计算工序模拟加工的开始时间
        '    LMAscSLResStartDate(reourceId, simulateStart, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        '    simulateEnd = simulateStart
        '    '计算工序模拟加工的结束时间
        '    LMAscSLResEndDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)

        '    resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
        '    resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

        '    '计算设备模拟加工的开始时间
        '    'LMAscSLResStartDate(reourceId, simulateStart2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        '    simulateStart2 = simulateStart
        '    simulateEnd2 = simulateStart2
        '    '计算设备模拟加工的结束时间
        '    LMAscSLResEndDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCyCle2, True)

        '    resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
        '    resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
        '    resultRow("qty") = dr("qty").ToString
        '    result.Rows.Add(resultRow)
        'Next

        result.AcceptChanges()

        Return result

    End Function

    ''' <summary>
    ''' 设备模拟加工数据追加优先级，分配人员
    ''' </summary>
    ''' <param name="resSimulationDt">设备模拟加工数据</param>
    ''' <param name="stepReourceDt">工序设备</param>
    ''' <param name="stepEmp">工序人员</param>
    ''' <param name="stepResMKDt">设备日历信息</param>
    ''' <param name="resShiftDt">设备班次信息</param>
    ''' <param name="alreadyConIds">本次已排产批次ids</param> 
    ''' <param name="apsType">排产策略</param>
    ''' <param name="makeEmpCounts">工序加工人数</param>
    ''' <param name="isMachineParallel">设备是否并行加工</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function LMAscSLResEmp(ByVal resSimulationDt As DataTable, ByVal stepReourceDt As DataTable, ByVal stepEmp As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                           ByVal resShiftDt As DataTable, ByVal alreadyConIds As List(Of String), ByVal apsType As String, ByVal makeEmpCounts As Integer, Optional ByVal isMachineParallel As String = "0",
                           Optional apsTempTable As String = "") As DataTable

        '此时应取出模拟加工结束最近的设备，把优先级追加上
        Dim recentEndDate = Nothing
        If Not resSimulationDt.Columns.Contains("prioritynum") Then
            resSimulationDt.Columns.Add(New DataColumn("prioritynum", System.Type.GetType("System.Int32")))
        End If

        For Each resSl In resSimulationDt.Rows
            '追加优先级
            If stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'").Length > 0 Then
                resSl("prioritynum") = stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'")(0)("prioritynum")
            End If
            If recentEndDate = Nothing Then
                recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
            Else
                If Date.Parse(resSl("simulateEnd").ToString) < recentEndDate Then
                    recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
                End If
            End If
        Next
        resSimulationDt.AcceptChanges()

        '筛选加工时间等于最近时间，并优先级最大的设备
        Dim recResSimulationDt As DataTable = resSimulationDt.Clone '最近时间加工的

        '设备并行加工时此处不过滤设备
        If isMachineParallel = "0" Then
            For Each resSl In resSimulationDt.Rows
                If Date.Parse(resSl("simulateEnd").ToString) = recentEndDate Then
                    recResSimulationDt.ImportRow(resSl)
                    Exit For
                End If
            Next
        Else
            recResSimulationDt = resSimulationDt.Copy
        End If

        Dim recResSimulationView = recResSimulationDt.DefaultView()
        recResSimulationView.Sort = "prioritynum desc"
        recResSimulationDt = recResSimulationView.ToTable()
        recResSimulationDt.AcceptChanges()

        '设备模拟完成，分配到人

        '排除设备模拟加工时间段内，所有人都被占用
        '人员信息，即工序能加工的人员信息
        recResSimulationDt.Columns.Add("employeeids") '满足加工的人员信息


        '人员占用情况，查询已下发的计划，本次已排产的批次占用,还有休假时间都算占用
        Dim empAps = <string>select ae.employeeid,ad.starttime,ad.endtime from employee e 
left join ApsPlanDetailEmp ae on ae.employeeid=e.employeeid and ae.isuse=0
left join ApsPlanDetail ad on ad.apsplandetailid=ae.apsplandetailid
left join apsplaninfo ai on ai.apsplaninfoid=ad.apsplaninfoid and ai.isuse=0
where ad.isuse=0 and ae.isuse=0 and ae.employeeid in ({0}) and nvl(e.isparallel,'0')='0' 
AND NOT EXISTS (SELECT 1 FROM containersteprecord csr where csr.containerid=ad.containerid and csr.workflowstepid=ad.workflowstepid and csr.usecompleted=1)
and 
(
ad.Status=1 
or(
ad.Status=0 and ad.containerid in ({3}) and ai.apstype2=2 and ai.apstype={4}
) 
) </string>.Value

        '--starttime，leavestartdate 开始时间 
        'endtime，leaveenddate 结束时间   
        'starttime>to_date('{2}','yyyy/mm/dd hh24:mi:ss')--小于开始时间
        'endtime<to_date('{1}','yyyy/mm/dd hh24:mi:ss')--大于结束时间
        empAps = empAps +
            " and not (" +
            " ad.endtime<to_date('{1}','yyyy/mm/dd hh24:mi:ss') " +
            " or ad.starttime>to_date('{2}','yyyy/mm/dd hh24:mi:ss') " +
            " ) "

        '用此sql查询人员占用，测试性能 add:wangjh 20240830
        Dim empAps3 = <string>with ae as 
(
select ae.employeeid,ae.apsplandetailid from employee e 
inner join ApsPlanDetailEmp ae on ae.employeeid=e.employeeid and ae.isuse=0
where  e.employeeid ='{0}' and nvl(e.isparallel,'0')='0' 
)
select ae.employeeid,ad.starttime,ad.endtime from ae ae
inner join ApsPlanDetail ad on ad.apsplandetailid=ae.apsplandetailid and ad.isuse=0
where ad.Status=1  </string>.Value


        Dim empAps3Condition = " and not ( ad.endtime<to_date('{1}','yyyy/mm/dd hh24:mi:ss') " +
" or ad.starttime>to_date('{2}','yyyy/mm/dd hh24:mi:ss') " +
" ) AND NOT EXISTS (SELECT 1 FROM containersteprecord csr " +
" where csr.containerid=ad.containerid and csr.workflowstepid=ad.workflowstepid and csr.usecompleted=1)"

        empAps3 += empAps3Condition

        If Not String.IsNullOrWhiteSpace(apsTempTable) Then
            empAps3 += " union all select  ae.employeeid,ad.starttime,ad.endtime from ae ae "
            empAps3 += " inner join " + apsTempTable + " ad on ad.apsplandetailid=ae.apsplandetailid and ad.isuse=0 "
            empAps3 += " where 1=1 "
            empAps3 += empAps3Condition
        End If


        Dim empAps2 = " select el.employeeid,el.leavestartdate starttime,el.leaveenddate endtime from EmployeeLeaveInfo el" +
            " where el.employeeid in ({0})" +
            " and not (" +
            " el.leaveenddate<to_date('{1}','yyyy/mm/dd hh24:mi:ss')" +
            " or" +
                " el.leavestartdate>to_date('{2}','yyyy/mm/dd hh24:mi:ss')" +
            ")"
        '人员日历占用情况 add:Wanjh 2023/2/16
        Dim empMK = <string>select e.employeeid,md.holidaydate from employee e
left join MakeCalendar mc on mc.makecalendarid=e.makecalendarid and mc.isuse=0
left join MakeCalendarDetail md on md.makecalendarid=mc.makecalendarid and md.isuse=0
where e.employeeid in ({0}) and nvl2(md.makecalendarid,'1','0')='1'
and md.holidaydate between to_date('{1}','yyyy/mm/dd hh24:mi:ss') and to_date('{2}','yyyy/mm/dd hh24:mi:ss')
group by e.employeeid,md.holidaydate</string>.Value

        '人员班次信息
        Dim empShift = <string>select e.employeeid,wf.startdate,wf.enddate,wd.startdate startdatedetail,wd.enddate enddatedetail,wd.mins,wd.sequence
 from employee e
left join workshift wf on wf.workshiftid=e.workshiftid
left join workshiftdetail wd on wd.workshiftid=e.workshiftid
where wf.isuse=0 and e.employeeid in ({0}) </string>.Value

        '人员排班情况
        Dim empWorkDate = <string>select aw.employeeid,to_char(aw.workdate,'yyyy-mm-dd') workdate,wf.startdate,wf.enddate,wd.startdate startdatedetail,wd.enddate enddatedetail,wd.mins,wd.sequence
 from ApsEmployeeWorkDate aw
left join workshift wf on wf.workshiftid=aw.workshiftid
left join workshiftdetail wd on wd.workshiftid=aw.workshiftid
where aw.employeeid in ({0}) and aw.workdate between to_date('{1}','yyyy/mm/dd') 
and to_date('{2}','yyyy/mm/dd')</string>.Value

        For Each resSLRow In recResSimulationDt.Rows

            Dim resShiftMin = "" '设备最早开始班次时间
            Dim resShiftMax = "" '设备最晚结束班次时间

            Dim resShiftDrs = resShiftDt.Select("resourceid='" + resSLRow("resourceid").ToString + "'") '此设备的班次信息
            If resShiftDrs.Length > 0 Then
                resShiftMin = resShiftDrs(0)("startdate").ToString
                resShiftMax = resShiftDrs(0)("enddate").ToString
            End If

            If stepEmp.Select("resourceid='" + resSLRow("resourceid").ToString + "'").Length = 0 Then
                Continue For
            End If
            Dim _simulateEnd = Date.Parse(resSLRow("simulateEnd").ToString)
            Dim _simulateStart = Date.Parse(resSLRow("simulateStart").ToString)

            '此设备上的所有人
            Dim resEmp = stepEmp.Select("resourceid='" + resSLRow("resourceid").ToString + "'")
            Dim resEmps = ""
            For Each em In resEmp
                resEmps += "'" + em("employeeid").ToString + "',"
            Next
            resEmps = resEmps.Trim(",")
            If String.IsNullOrWhiteSpace(resEmps) Then '此设备没人
                Continue For
            End If

            '此设备的日历信息
            Dim _stepResMKDt As DataTable = faMakeCalendarDt.Copy
            If stepResMKDt.Select("resourceid='" + resSLRow("resourceid").ToString + "'").Length > 0 Then
                _stepResMKDt = stepResMKDt.Select("resourceid='" + resSLRow("resourceid").ToString + "'").CopyToDataTable
            End If

            '查询此设备因下发，请假受影响的人
            Dim apsOccupyEmpDT = DatabaseHelper.Query_ReturnDatTable(String.Format(empAps2, resEmps, resSLRow("simulateStart").ToString, resSLRow("simulateEnd").ToString))
            'If alreadyConIds.Count > 999 Then
            '    Dim pCount = Math.Ceiling(alreadyConIds.Count / 999) '页数
            '    For i As Integer = 0 To pCount - 1
            '        Dim _conIdList = alreadyConIds.Skip(i * 999).Take(999).ToList
            '        apsOccupyEmpDT.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(empAps, resEmps, resSLRow("simulateStart").ToString, resSLRow("simulateEnd").ToString,
            '                                                                           "'" + String.Join(",", _conIdList).Replace(",", "','") + "'", apsType)))
            '    Next
            'Else
            '    apsOccupyEmpDT.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(empAps, resEmps, resSLRow("simulateStart").ToString, resSLRow("simulateEnd").ToString,
            '                                                                           "'" + String.Join(",", alreadyConIds).Replace(",", "','") + "'", apsType)))
            'End If

            '查询此设备因日历，受影响的人
            Dim apsEmpMKDt = DatabaseHelper.Query_ReturnDatTable(String.Format(empMK, resEmps, resSLRow("simulateStart").ToString, resSLRow("simulateEnd").ToString))

            '查询人员的班次情况
            Dim empShiftDt = DatabaseHelper.Query_ReturnDatTable(String.Format(empShift, resEmps))

            '查询此设备人的排班情况
            Dim empWDDt = DatabaseHelper.Query_ReturnDatTable(String.Format(empWorkDate, resEmps, _simulateStart.ToString("yyyy-MM-dd"), _simulateEnd.ToString("yyyy-MM-dd")))

            '如果此人员参与排班，按排班进行筛选。否则按日历和班次筛选

            Dim availableEmpList = New List(Of String) '此设备可用的人员
            Dim occupyEmpList = New List(Of String) '此设备已占用受影响的人员(包含休假，工作班次不符)
            For Each em In resEmp

                apsOccupyEmpDT.Merge(DatabaseHelper.Query_ReturnDatTable(String.Format(empAps3, em("employeeid").ToString, resSLRow("simulateStart").ToString, resSLRow("simulateEnd").ToString)))

                Dim empShiftMin = "" '人员最早开始班次时间
                Dim empShiftMax = "" '人员最晚结束班次时间

                Dim empShiftDrs = empShiftDt.Select("employeeid='" + em("employeeid").ToString + "'")
                If empShiftDrs.Length > 0 Then
                    empShiftMin = empShiftDrs(0)("startdate").ToString
                    empShiftMax = empShiftDrs(0)("enddate").ToString
                End If

                If apsOccupyEmpDT.Select("employeeid='" + em("employeeid").ToString + "'").Length > 0 Then '此人已被占用
                    occupyEmpList.Add(em("employeeid").ToString)
                Else

                    If em("IsSchedule").ToString <> "1" Then '此人不参与排班
                        If apsEmpMKDt.Select("employeeid='" + em("employeeid").ToString + "'").Length > 0 Then '日历休假，受影响
                            occupyEmpList.Add(em("employeeid").ToString)
                        ElseIf WorkShiftIncludeOtherShift(empShiftMin, empShiftMax, resShiftMin, resShiftMax) = False Then '班次对不上，受影响
                            occupyEmpList.Add(em("employeeid").ToString)
                        Else
                            availableEmpList.Add(em("employeeid").ToString)
                        End If
                    Else '此人参与排班
                        Dim _empWDDt = empWDDt.Clone

                        If empWDDt.Select("employeeid='" + em("employeeid").ToString + "'").Length > 0 Then
                            _empWDDt = empWDDt.Select("employeeid='" + em("employeeid").ToString + "'").CopyToDataTable
                        End If

                        If ResDateMatchEmpWorkDate(_empWDDt, _stepResMKDt, resShiftDt, _simulateStart, _simulateEnd) Then
                            availableEmpList.Add(em("employeeid").ToString)
                        Else
                            occupyEmpList.Add(em("employeeid").ToString)
                        End If

                    End If

                End If

            Next

            If availableEmpList.Count = 0 Then '此设备下所有人都没空？？？

            End If

            If availableEmpList.Count > 0 Then
                If makeEmpCounts = 1 Then
                    resSLRow("employeeids") = availableEmpList(0)
                ElseIf makeEmpCounts > 1 Then
                    If availableEmpList.Count >= makeEmpCounts Then
                        For i As Integer = 0 To makeEmpCounts - 1
                            resSLRow("employeeids") += availableEmpList(i) + ","
                        Next
                    Else '空闲出来的人数<工序设定的加工人数
                        resSLRow("employeeids") = String.Join(",", availableEmpList.ToArray())
                        '按优先级再追加剩余的人数
                        For i As Integer = 0 To makeEmpCounts - availableEmpList.Count - 1
                            If occupyEmpList.Count > i Then 'yuxj
                                resSLRow("employeeids") += "," + occupyEmpList(i)
                            End If

                        Next

                    End If
                End If
                resSLRow("employeeids") = resSLRow("employeeids").ToString().Trim(",")
            End If


        Next

        recResSimulationDt.AcceptChanges()

        Return recResSimulationDt
    End Function

    ''' <summary>
    ''' 设备模拟加工数据追加优先级，分配人员
    ''' 不分配到人,此方法已删除分配到人的逻辑
    ''' </summary>
    ''' <param name="resSimulationDt">设备模拟加工数据</param>
    ''' <param name="stepReourceDt">工序设备</param>
    ''' <param name="stepEmp">工序人员</param>
    ''' <param name="makeEmpCounts">工序加工人数</param>
    ''' <param name="isMachineParallel">设备是否并行加工</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function LMAscSLResEmp(ByVal resSimulationDt As DataTable, ByVal stepReourceDt As DataTable, ByVal stepEmp As DataTable, ByVal makeEmpCounts As Integer, Optional ByVal isMachineParallel As String = "0") As DataTable
        '此时应取出模拟加工结束最近的设备，把优先级追加上
        Dim recentEndDate = Nothing
        If Not resSimulationDt.Columns.Contains("prioritynum") Then
            resSimulationDt.Columns.Add(New DataColumn("prioritynum", System.Type.GetType("System.Int32")))
        End If

        For Each resSl In resSimulationDt.Rows
            '追加优先级
            If stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'").Length > 0 Then
                resSl("prioritynum") = stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'")(0)("prioritynum")
            End If
            If recentEndDate = Nothing Then
                recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
            Else
                If Date.Parse(resSl("simulateEnd").ToString) < recentEndDate Then
                    recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
                End If
            End If
        Next
        resSimulationDt.AcceptChanges()

        '筛选加工时间等于最近时间，并优先级最大的设备
        Dim recResSimulationDt As DataTable = resSimulationDt.Clone '最近时间加工的

        '设备并行加工时此处不过滤设备
        If isMachineParallel = "0" Then
            For Each resSl In resSimulationDt.Rows
                If Date.Parse(resSl("simulateEnd").ToString) = recentEndDate Then
                    recResSimulationDt.ImportRow(resSl)
                    Exit For
                End If
            Next
        Else
            recResSimulationDt = resSimulationDt.Copy
        End If

        Dim recResSimulationView = recResSimulationDt.DefaultView()
        recResSimulationView.Sort = "prioritynum desc"
        recResSimulationDt = recResSimulationView.ToTable()
        recResSimulationDt.AcceptChanges()

        '设备模拟完成，分配到人

        '排除设备模拟加工时间段内，所有人都被占用
        '人员信息，即工序能加工的人员信息
        recResSimulationDt.Columns.Add("employeeids") '满足加工的人员信息



        recResSimulationDt.AcceptChanges()

        Return recResSimulationDt
    End Function
#End Region

#Region "有限产能，倒排"
    ''' <summary>
    ''' 有限产能，倒排
    ''' 日历：分厂日历>设备日历>人员日历 --颗粒度变细
    ''' 班次：设备班次>人员班次 --颗粒度变细
    ''' yuxj20221026 增加记录设备开始和结束时间，设备加工时间 包含工序准备时间 和 加工时间，不含转运时间
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="IsBottleneck">是否是瓶颈排中的倒排</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function LMReverseCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim apsType = "2"
        If IsBottleneck Then
            apsType = "3"
        End If

        Dim alreadyApsConIdList As New List(Of String) '本排产池本次已经排产的批次id列表
        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim stepDt As New DataTable
            If conDt.Columns.Contains("ENDSEQUENCE") Then
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString, 0, Val(r("ENDSEQUENCE").ToString))
            Else
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString)  '工序
            End If

            '如果批次工序已经完工，筛选掉完工的工序 
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If IsBottleneck Then
                If stepDt.Rows.Count = 0 Then
                    result.Result = True
                    Return result
                End If
            Else
                If stepDt.Rows.Count = 0 Then
                    result.Message = "批次" + r("containername").ToString + "未找到可排产的工序"
                    'Return result
                    'Continue For '执行后面的批次yuxj20230307
                    GoTo conEnd
                End If
            End If

            Dim endTime = Date.Parse(r("ENDTIME"))
            'endTime.AddDays(GetDateOffset(2, endTime, faMakeCalendarDt, 0)) '检查工作日历

            If endTime = endTime.Date Then
                endTime = endTime.AddMinutes(1439) '初期结束时间为23:59:00
            End If

            Dim curTime = endTime

            Dim stepCycle As Single = 0 '工序加工周期，单位分钟
            Dim stepCyCle2 As Single = 0 'yuxj设备加工周期，单位分钟
            Dim stepPlanSqls As New ArrayList

            For j As Integer = stepDt.Rows.Count - 1 To 0 Step -1
                Dim stepRow = stepDt.Rows(j)

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                stepCycle = 0 '工序加工周期，单位分钟

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "批次" + r("containername").ToString + "工序" + stepRow("workflowstepname").ToString + "无设备"
                        'Return result
                        Exit For
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "批次" + r("containername").ToString + "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        'Return result
                        Exit For
                    End If
                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT = GetResOccupyDtBeforeTime(stepResourceIds, alreadyApsConIdList, curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()

                '此工序设备当前时间前的所有保养信息
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtBeforeTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))

                '此工序设备所有的日历信息
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)

                '此工序设备所有的班次信息
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)

                Dim _startTime = curTime '此工序的初期开始时间

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMDescSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, faMakeCalendarDt, stepResMKDt, stepShiftDt, alreadyApsConIdList, apsType, makeEmpCounts, isMachineParallel)
                'yuxj30230309多设备并行逻辑处理
                Dim strFilter = "workflowstepid='" + stepRow("workflowstepid").ToString + "'"
                Dim stepApsRows = stepApsDt.Select(strFilter)
                '为保证旧的排产明细id可以使用，此处逻辑改为如果设备个数超过工序记录数，超出的的设备记录调用插入功能
                '如果设备个数小于工序记录数，超出的的工序记录调用删除功能
                Dim delStepApsId As String = ""
                If stepApsRows.Length > recResSimulationDt.Rows.Count Then
                    '超过的Apsplandetailid记录执行删除
                    For i As Integer = recResSimulationDt.Rows.Count - 1 To stepApsRows.Length - 1
                        delStepApsId = stepApsRows(i)("Apsplandetailid").ToString
                        stepPlanSqls.Add(String.Format(Sql_StepApsDel, delStepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, delStepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, delStepApsId))
                    Next
                End If

                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    Dim confirmResSL As DataRow = recResSimulationDt.Rows(i)
                    'yuxj30230309 不能只取第一行，不过有设备没有人时如何处理？？ 本逻辑不插入人员
                    'If recResSimulationDt.Select("employeeids is not null").Length > 0 Then
                    '    confirmResSL = recResSimulationDt.Select("employeeids is not null")(0)
                    'End If

                    '存入最佳设备和人员到排产计划表                    
                    '生成工序计划sql
                    curTime = Date.Parse(confirmResSL("simulateStart"))

                    '存入最佳设备和人员到排产计划表
                    '生成工序计划sql

                    Dim stepApsId As String = ""
                    If i <= stepApsRows.Length - 1 Then
                        stepApsId = stepApsRows(i)("Apsplandetailid")
                        stepPlanSqls.Add(String.Format(Sql_StepApsUpdate, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty"), conApsID, stepRow("workflowstepid").ToString, stepApsId))

                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, stepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, stepApsId))
                    Else
                        stepApsId = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepApsInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                 stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), para("EmployeeID"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty"), stepRow("specid").ToString))

                    End If



                    For Each _emp In confirmResSL("employeeids").ToString.Split(",")
                        If String.IsNullOrWhiteSpace(_emp) Then
                            Continue For
                        End If

                        'apsplandetailempid特别处理
                        Dim apsplandetailempid As String = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsInsert, stepApsId, _emp, apsplandetailempid))

                        'yuxj20220823热表数据 增加apsplandetailemptime的插入
                        If stepRow("attributevalue").ToString() = "602" Then '会不会改名或多个包含热表字段的数据？
                            AddEmployeeTime(stepPlanSqls, _emp, apsplandetailempid, confirmResSL)
                        Else
                            stepPlanSqls.Add(String.Format(Sql_EmpTimeApsInsert, apsplandetailempid, Guid.NewGuid.ToString(), confirmResSL("simulateStart"), confirmResSL("simulateEnd"), _emp))
                        End If
                    Next
                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next

            Next
            '执行存入工序
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))

            If conApsDt2.Rows.Count > 0 Then

                '生成批次计划sql
                stepPlanSqls.Clear()
                If conApsDt.Rows.Count > 0 Then
                    stepPlanSqls.Add(String.Format(Sql_ConApsUpdate, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), conApsID))
                Else
                    stepPlanSqls.Add(String.Format(Sql_ConApsInsert, conApsID, apsType, r("containerid").ToString, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                                   r("workflowid").ToString))
                End If
                '执行存入批次
                DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            End If
            '存储已经排产的批次id子串，
            If Not alreadyApsConIdList.Contains(r("containerid").ToString) And conBaseRow("containertype").ToString <> "7" Then
                alreadyApsConIdList.Add(r("containerid").ToString)
            End If
conEnd:
            If Not String.IsNullOrWhiteSpace(result.Message) Then
                m_containerObj.WriteContainerSCLog("", r("containerid").ToString, r("containername").ToString, result.Message, 1, para("EmployeeID"))
            Else
                m_containerObj.DeleteContainerSCLog(r("containerid").ToString, 1)
            End If
        Next

        result.Result = True
        Return result

    End Function

    ''' <summary>
    ''' 倒排模拟加工,工序各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="startTime">工序初期开始时间</param>
    ''' <param name="endTime">工序初期结束时间</param>  
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMDescSimulationByRes(ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date) As DataTable

        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间

        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备分配的数量：多机并行时按数量分配，非多机并行时各设备数量相同

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCycle2 As Single = 0 '增加设备加工周期
        For Each dr As DataRow In resourceTable.Rows
            Dim reourceId As String = dr("resourceid").ToString
            stepCycle = Single.Parse(dr("stepCycle").ToString)
            stepCycle2 = Single.Parse(dr("stepCyCle2").ToString)

            Dim simulateStart = startTime '模拟开始时间
            Dim simulateEnd = endTime '模拟结束时间

            Dim simulateStart2 = startTime '设备模拟开始时间
            Dim simulateEnd2 = endTime '设备模拟结束时间

            Dim resultRow As DataRow = result.NewRow
            resultRow("resourceid") = reourceId

            '+设备本身日历
            Dim _stepResMKDt = faMakeCalendarDt.Copy
            If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
                _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
            End If

            Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
            Dim _stepShiftDt = stepShiftDt.Clone
            Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
            If _stepShiftDrs.Length > 0 Then
                _stepShiftDt = _stepShiftDrs.CopyToDataTable
                resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
            End If

            '----计算设备的工作周期(分钟)
            Dim resCycle = 1439
            If _stepShiftDt.Rows.Count > 1 Then
                resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
            End If

            '此设备的保养计划
            Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")

            Dim _stepResUpkeepDt As New DataTable
            If resUpKeepRows.Length > 0 Then
                _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
            End If

            '此设备占用数据
            Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
            Dim _apsOccupyResDT As New DataTable
            If resApsOccupyRows.Length > 0 Then
                _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
            End If


            '计算工序模拟加工结束时间
            LMDescSLResEndDate(reourceId, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)

            simulateStart = simulateEnd

            '计算工序模拟加工开始时间
            LMDescSLResStartDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)

            resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

            '计算设备模拟加工的结束时间,往前算去除转运时间stepCycle-stepCycle2
            simulateEnd2 = simulateEnd
            simulateStart2 = simulateEnd
            LMDescSLResStartDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle - stepCycle2, True)
            simulateEnd2 = simulateStart2

            '计算设备模拟加工的开始时间
            simulateStart2 = simulateEnd2
            LMDescSLResStartDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle2, True)

            resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("qty") = dr("qty").ToString

            result.Rows.Add(resultRow)
        Next

        result.AcceptChanges()

        Return result

    End Function

    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Desc:倒排，SL:模拟运算，EndDate:结束时间 
    ''' </summary>
    ''' <param name="resourceId"></param>
    ''' <param name="simulateEnd">设备模拟加工结束时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次详细信息eg:8:30-12:00,13:00-15:00,17:30-19:30</param>
    ''' <param name="resShiftStart">设备班次最早时间eg:8:30</param>
    ''' <param name="resShiftEnd">设备班次最晚时间eg:19:30</param>
    ''' <param name="resCycle">设备实际运转周期(去除休息时间)</param>
    ''' <remarks></remarks>
    Sub LMDescSLResEndDate(ByVal resourceId As String, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                            ByVal resCycle As Integer)

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateEnd = simulateEnd.AddDays(GetDateOffset(2, simulateEnd, stepResMKDt, 0)) '检查工作日历,结束时间向前顺移

        '班次因素
        Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备初期结束时间

        '------------更新班次最晚时间 add by zhangrj 20250923----------------
        Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateEnd) '获取设备在这一天的新的班次明细
        For Each drd As DataRow In dtDayWorkshift.Rows
            'Dim rStart = GetRunStartOrEndDate(simulateEnd, drd("starttime")) '开始时间
            Dim rEnd = GetRunStartOrEndDate(simulateEnd, drd("endtime")) '结束时间
            If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                resEnd = rEnd
            End If
        Next
        '----------------------------

        Dim differ = GetDifferMins(resEnd, simulateEnd)

        If differ < 0 Then '工序结束19：30 ，设备默认结束18：30
            simulateEnd = resEnd
        ElseIf differ <= resCycle Then '工序结束时间在设备周期内,工序结束：17：30，而设备结束18：30,,此时要考虑休息时间段如果17:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateEnd = GetResApsStartOrEndDate(1, simulateEnd, _stepShiftDt, dtDayWorkshift)
            End If

        Else '此情况例子，工序结束时间：6：30，而设备开始时间8：30.设备结束时间18：30，真正结束时间就要向前推一天，跨天考虑日历

            simulateEnd = resEnd
            simulateEnd = simulateEnd.AddDays(-1)

            'simulateEnd = simulateEnd.AddDays(GetDateOffset(2, simulateEnd, stepResMKDt, 0))
            ''减去剩余的分钟数
            'simulateEnd = simulateEnd.AddMinutes(resCycle - differ)

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````}````]或者 '[`````{````}```]情况下才会让 结束时间向前偏移

        Dim resUpKeepOffset1 As Single = 0 '保养检查后，需要偏移的分钟数，开始时间
        Dim lastUpkeepStartTime = simulateEnd '设备最新保养占用的最小时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟结束时间在保养周期内才会偏移
            If Not (lastUpkeepStartTime >= upkeepStart And lastUpkeepStartTime < upkeepEnd) Then
                Continue For
            End If
            '必须小于上次占用最早时间才偏移
            If upkeepEnd >= lastUpkeepStartTime Then
                Continue For
            End If

            resUpKeepOffset1 -= GetDifferMins(lastUpkeepStartTime, upkeepStart)
            lastUpkeepStartTime = upkeepStart
        Next

        simulateEnd = simulateEnd.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况
        Dim resApsOccupyOffset1 As Single = 0 '排产占用后，需要偏移的分钟数 
        Dim lastOccupyStartTime = simulateEnd '设备最新占用的最小时间
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("resstarttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("resendtime").ToString)

            '模拟结束时间在占用周期内才会偏移
            If Not (lastOccupyStartTime >= apsStartTime And lastOccupyStartTime < apsEndTime) Then
                Continue For
            End If
            '必须小于上次占用最早时间才偏移
            If apsEndTime >= lastOccupyStartTime Then
                Continue For
            End If

            resApsOccupyOffset1 -= GetDifferMins(lastOccupyStartTime, apsStartTime)
            lastOccupyStartTime = apsStartTime
        Next

        simulateEnd = simulateEnd.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            LMDescSLResEndDate(resourceId, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        End If

    End Sub

    ''' <summary>
    ''' 倒排设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Desc:倒排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="resourceId"></param>
    ''' <param name="simulateStart">模拟开始时间</param>
    ''' <param name="simulateEnd">模拟结束时间</param>
    ''' <param name="stepResMKDt">设备日历</param>
    ''' <param name="resUpKeepDt">设备保养</param>
    ''' <param name="_apsOccupyResDT">设备占用</param>
    ''' <param name="_stepShiftDt">设备班次</param>
    ''' <param name="resShiftStart">设备班次最早开始时间</param>
    ''' <param name="resShiftEnd">设备班次最晚结束时间</param>
    ''' <param name="resCycle">设备运转周期（排除休息时间）</param>
    ''' <param name="stepCycle">工序(设备)需要加工周期</param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMDescSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                             ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                             ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal isFirst As Boolean)
        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(2, simulateStart, stepResMKDt, 1)
                    simulateStart = simulateStart.AddDays(offset - 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateStart = GetResApsEndOrStartDate(2, simulateStart, resRunCycle2, _stepShiftDt, stepResMKDt, resourceId)
        Else '递归进来的情况
            simulateStart = simulateStart.AddDays(GetDateOffset(2, simulateStart, stepResMKDt, 0))

            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备开始时间

            '------------更新班次最晚时间 add by zhangrj 20250923----------------
            Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateStart) '获取设备在这一天的新的班次明细
            For Each drd As DataRow In dtDayWorkshift.Rows
                Dim rStart = GetRunStartOrEndDate(simulateEnd, drd("starttime")) '开始时间
                Dim rEnd = GetRunStartOrEndDate(simulateEnd, drd("endtime")) '结束时间
                If resStart > rStart Then '如果设备初始开始时间大于该班次的结束时间，则更新设备初始开始时间
                    resStart = rStart
                End If
                If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                    resEnd = rEnd
                End If
            Next
            '----------------------------

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resStart = resStart.AddDays(-1)
            End If

            If simulateStart < resStart Then '例如：工序7:30开始，而设备8：30才开始
                Dim differ = GetDifferMins(simulateStart, resStart)

                simulateStart = resEnd
                simulateStart = simulateStart.AddDays(-1)
                simulateStart = simulateStart.AddDays(GetDateOffset(2, simulateStart, stepResMKDt, 0))

                simulateStart = GetResApsEndOrStartDate(2, simulateStart, differ, _stepShiftDt, stepResMKDt, resourceId)
            ElseIf simulateStart > resEnd Then '例如：工序19：30开始，而设备18：30就已经结束
                Dim differ = GetDifferMins(resEnd, simulateStart)
                simulateStart = GetResApsEndOrStartDate(2, simulateStart, differ, _stepShiftDt, stepResMKDt, resourceId)
            Else '正常情况，开始时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateStart = GetResApsStartOrEndDate(2, simulateStart, _stepShiftDt, dtDayWorkshift)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '[````{`````]````}  情况下才会只让 开始时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1

            Dim resUpKeepRow = resUpKeepDt.Rows(i)
            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If simulateEnd > upkeepEnd And simulateStart < upkeepEnd Then

                If simulateStart > upkeepStart Then '[````{`````]````}                   
                    ' resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)
                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf simulateStart < upkeepStart Then '{`````[````]````} 
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)
                    resUpKeepDt.Rows.RemoveAt(i)
                End If

                '计算实际占用分钟数，考虑班次，休息时间
                Dim _resUpKeepOffset2 = GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

                simulateStart = GetResApsEndOrStartDate(2, simulateStart, _resUpKeepOffset2, _stepShiftDt, stepResMKDt, resourceId)
                resUpKeepOffset2 += _resUpKeepOffset2
            End If

        Next
        'simulateStart = GetResApsEndOrStartDate(2, simulateStart, resUpKeepOffset2, _stepShiftDt, stepResMKDt)

        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？ 
        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是计算出占用设备的实际分钟数
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1

            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If simulateEnd > apsEndTime And simulateStart < apsEndTime Then
                If simulateStart > apsStartTime Then '[````{`````]````}
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf simulateStart < apsStartTime Then '{`````[````]````} 
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                Dim _resApsOccupyOffset2 = GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

                simulateStart = GetResApsEndOrStartDate(2, simulateStart, _resApsOccupyOffset2, _stepShiftDt, stepResMKDt, resourceId)
                resApsOccupyOffset2 += _resApsOccupyOffset2
            End If
        Next
        'simulateStart = GetResApsEndOrStartDate(2, simulateStart, resApsOccupyOffset2, _stepShiftDt, stepResMKDt)

        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            LMDescSLResStartDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd,
                                 resCycle, stepCycle, False)
        End If

    End Sub

#End Region

#Region "有限产能，瓶颈排"
    ''' <summary>
    ''' 有限产能，瓶颈排
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function LMBottleneckCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False
        Dim apsType = "3"

        Dim isError = False

        Dim alreadyApsConIdList As New List(Of String)  '本排产池本次已经排产的批次id列表
        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim startTime = Date.Parse(r("STARTTIME"))
            startTime = startTime.AddDays(GetDateOffset(1, startTime, faMakeCalendarDt, 0)) '检查工作日历

            Dim stepDt As DataTable = Aps_GetStepBaseDt(r("workflowid").ToString, 0, 0, 1) '工序

            '如果批次工序已经完工，筛选掉完工的工序 
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, "3")) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If stepDt.Rows.Count = 0 Then
                result.Message = "批次" + r("containername").ToString + "未找到可排产的瓶颈工序"
                'Return result
                Continue For
            End If

            Dim curTime = startTime

            '先排瓶颈设备所在工序

            Dim stepPlanSqls As New ArrayList
            Dim bottleneckStartTime = "", bottleneckEndTime = "" '瓶颈开始和结束

            For Each stepRow As DataRow In stepDt.Rows

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                Dim _startTime = curTime '此工序的初期开始时间

                '_startTime.AddDays(GetDateOffset(1, _startTime, faMakeCalendarDt, 0)) '检查工作日历,开始时间顺移

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "批次" + r("containername").ToString + "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "批次" + r("containername").ToString + "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        Return result
                    End If
                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, alreadyApsConIdList, curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()

                '此工序设备的所有保养信息
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))

                '此工序设备所有的日历信息
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)

                '此工序设备所有的班次信息
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                '判断瓶颈设备的模拟开始日期是否在范围内
                If Not (Date.Parse(r("STARTTIME")) <= Date.Parse(resSimulationDt.Rows(0)("simulateStart").ToString) And Date.Parse(r("ENDTIME")) >= Date.Parse(resSimulationDt.Rows(0)("simulateStart").ToString)) Then
                    result.Message += "批次:" + conBaseRow("containername").ToString + "瓶颈设备的空闲开始时间为:" + resSimulationDt.Rows(0)("simulateStart").ToString + "不在订单的计划开始和结束时间内"
                    isError = True
                    Continue For
                End If

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, faMakeCalendarDt, stepResMKDt, stepShiftDt, alreadyApsConIdList, apsType, makeEmpCounts, isMachineParallel)
                'yuxj30230309多设备并行逻辑处理
                Dim strFilter = "workflowstepid='" + stepRow("workflowstepid").ToString + "'"
                Dim stepApsRows = stepApsDt.Select(strFilter)
                '为保证旧的排产明细id可以使用，此处逻辑改为如果设备个数超过工序记录数，超出的的设备记录调用插入功能
                '如果设备个数小于工序记录数，超出的的工序记录调用删除功能
                Dim delStepApsId As String = ""
                If stepApsRows.Length > recResSimulationDt.Rows.Count Then
                    '超过的Apsplandetailid记录执行删除
                    For i As Integer = recResSimulationDt.Rows.Count - 1 To stepApsRows.Length - 1
                        delStepApsId = stepApsRows(i)("Apsplandetailid").ToString
                        stepPlanSqls.Add(String.Format(Sql_StepApsDel, delStepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, delStepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, delStepApsId))
                    Next
                End If

                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    Dim confirmResSL As DataRow = recResSimulationDt.Rows(i)

                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                    '存入最佳设备和人员到排产计划表                    
                    '生成工序计划sql                   
                    curTime = Date.Parse(confirmResSL("simulateEnd"))

                    '存入最佳设备和人员到排产计划表
                    '生成工序计划sql
                    Dim stepApsId = ""
                    If i <= stepApsRows.Length - 1 Then
                        stepApsId = stepApsRows(0)("Apsplandetailid")
                        stepPlanSqls.Add(String.Format(Sql_StepApsUpdate, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty"), conApsID, stepRow("workflowstepid").ToString, stepApsId))

                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, stepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, stepApsId))
                    Else
                        stepApsId = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepApsInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                 stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), para("EmployeeID"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty"), stepRow("specid").ToString))

                    End If

                    For Each _emp In confirmResSL("employeeids").ToString.Split(",")
                        If String.IsNullOrWhiteSpace(_emp) Then
                            Continue For
                        End If

                        'apsplandetailempid特别处理
                        Dim apsplandetailempid As String = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsInsert, stepApsId, _emp, apsplandetailempid))

                        'yuxj20220823热表数据 增加apsplandetailemptime的插入
                        If stepRow("attributevalue").ToString() = "602" Then '会不会改名或多个包含热表字段的数据？
                            AddEmployeeTime(stepPlanSqls, _emp, apsplandetailempid, confirmResSL)
                        Else
                            stepPlanSqls.Add(String.Format(Sql_EmpTimeApsInsert, apsplandetailempid, Guid.NewGuid.ToString(), confirmResSL("simulateStart"), confirmResSL("simulateEnd"), _emp))
                        End If
                    Next
                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                    'bottleneckStartTime = confirmResSL("simulateStart")
                    'bottleneckEndTime = confirmResSL("simulateEnd")
                Next
                '多条设备并行时重新赋值
                bottleneckStartTime = recResSimulationDt.Rows(0)("simulateStart")
                bottleneckEndTime = recResSimulationDt.Rows(recResSimulationDt.Rows.Count - 1)("simulateEnd")

                Exit For
            Next

            If isError = True Then
                Continue For
            End If

            '执行存入工序
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)

            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))

            '生成批次计划sql
            stepPlanSqls.Clear()
            If conApsDt.Rows.Count > 0 Then
                stepPlanSqls.Add(String.Format(Sql_ConApsUpdate, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), conApsID))
            Else
                stepPlanSqls.Add(String.Format(Sql_ConApsInsert, conApsID, 3, r("containerid").ToString, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                               r("workflowid").ToString))
            End If
            '执行存入批次
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)

            '瓶颈工序排产完成，正排或倒排其他工序
            Dim DesConDt As DataTable = conDt.Clone
            If Not DesConDt.Columns.Contains("ENDSEQUENCE") Then
                DesConDt.Columns.Add("ENDSEQUENCE")
            End If
            DesConDt.ImportRow(r)
            DesConDt.Rows(0)("ENDSEQUENCE") = stepDt.Rows(0)("sequence")
            DesConDt.Rows(0)("ENDTIME") = bottleneckStartTime
            LMReverseCalculate(DesConDt, para, True)

            Dim AscConDt As DataTable = conDt.Clone
            If Not AscConDt.Columns.Contains("BEGINSEQUENCE") Then
                AscConDt.Columns.Add("BEGINSEQUENCE")
            End If
            AscConDt.ImportRow(r)
            AscConDt.Rows(0)("BEGINSEQUENCE") = stepDt.Rows(0)("sequence")
            AscConDt.Rows(0)("STARTTIME") = bottleneckEndTime
            LMPositiveCalculate(AscConDt, para, True)

            '存储已经排产的批次id子串，
            If Not alreadyApsConIdList.Contains(r("containerid").ToString) And conBaseRow("containertype").ToString <> "7" Then
                alreadyApsConIdList.Add(r("containerid").ToString)
            End If
        Next

        If Not isError Then
            result.Result = True
        End If
        Return result
    End Function
#End Region

#Region "排产调整"

    ''' <summary>
    ''' 排产调整
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function LMAPSChange(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim apsType = para("ApsType")

        '工序级排产信息sql
        Dim stepUpdateSql = <string> update ApsPlanDetail ad set ad.FeedbackDate=null,ad.ConfirmDate=null,ad.ConfirmEmpID=null,ad.LastChangeDate=sysdate,ad.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS'),ad.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
ad.workcenterid='{2}',ad.teamid='{3}',ad.resourceid='{4}' where ad.apsplandetailid='{5}' </string>.Value

        '先调整指定工序的计划，再调整其他工序计划
        Dim sqlList As New ArrayList
        sqlList.Add(String.Format(stepUpdateSql, para("StartTime"), para("EndTime"), para("WorkcenterID"), para("TeamID"), para("ResourceID"), para("ApsplanDetailID")))

        'yuxj热表增加删除apsplandetailemptime
        sqlList.Add(String.Format(Sql_EmpTimeApsDel, para("ApsplanDetailID")))
        sqlList.Add(String.Format(Sql_StepEmpApsDel, para("ApsplanDetailID")))

        Dim employeeIds = para("EmployeeIds")
        For Each emp As String In employeeIds.Split(",")
            sqlList.Add(String.Format(Sql_StepEmpApsInsert, para("ApsplanDetailID"), emp, Guid.NewGuid()))
        Next
        DatabaseHelper.ExecuteSqlTran(sqlList)

        If Not conDt.Columns.Contains("manualAdjust") Then '标记为是否手动调整后的重排
            conDt.Columns.Add("manualAdjust")
            conDt.Rows(0)("manualAdjust") = "1"
        End If

        If apsType = "1" Then '正排
            conDt.Columns.Add("BEGINSEQUENCE")
            conDt.Rows(0)("BEGINSEQUENCE") = para("Sequence")
            LMPositiveCalculate(conDt, para)
        ElseIf apsType = "2" Then '倒排
            conDt.Columns.Add("ENDSEQUENCE")
            conDt.Rows(0)("ENDSEQUENCE") = para("Sequence")
            LMReverseCalculate(conDt, para)
        ElseIf apsType = "3" Then '瓶颈排
            Dim stepDt As DataTable = Aps_GetStepBaseDt(conDt.Rows(0)("workflowid").ToString, 0, 0, 1)  '工序
            If stepDt.Rows.Count = 0 Then
                Return result
            End If
            Dim sequence = Val(para("Sequence"))
            If sequence > Val(stepDt.Rows(0)("sequence")) Then '顺排调整后续的
                conDt.Rows(0)("StartTime") = para("EndTime")
                conDt.Columns.Add("BEGINSEQUENCE")
                conDt.Rows(0)("BEGINSEQUENCE") = para("Sequence")
                LMPositiveCalculate(conDt, para, True)
            ElseIf sequence < Val(stepDt.Rows(0)("sequence")) Then '倒排调整前序的
                conDt.Rows(0)("EndTime") = para("StartTime")
                conDt.Columns.Add("ENDSEQUENCE")
                conDt.Rows(0)("ENDSEQUENCE") = para("Sequence")
                LMReverseCalculate(conDt, para, True)
            Else '调整的就是瓶颈设备,前后都要调整
                conDt.Rows(0)("StartTime") = para("EndTime")
                conDt.Columns.Add("BEGINSEQUENCE")
                conDt.Rows(0)("BEGINSEQUENCE") = para("Sequence")
                LMPositiveCalculate(conDt, para, True)

                conDt.Columns.Remove("BEGINSEQUENCE")
                conDt.Rows(0)("EndTime") = para("StartTime")
                conDt.Columns.Add("ENDSEQUENCE")
                conDt.Rows(0)("ENDSEQUENCE") = para("Sequence")
                LMReverseCalculate(conDt, para, True)
            End If
        End If


        result.Result = True
        Return result
    End Function

    ''' <summary>
    ''' 批次调度---排产调整
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function MoveNon_LMApsChange(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim apsIdSql As String = <string>select ai.apsplaninfoid from apsplandetail al
left join apsplaninfo ai on ai.apsplaninfoid=al.apsplaninfoid
left join workflowstep ws on ws.workflowstepid=al.workflowstepid
where  al.containerid='{0}' and ai.workflowid='{1}' and ws.sequence>={2} and (al.status='1' or ai.apstype='1' )
group by ai.apsplaninfoid</string>.Value

        For Each r As DataRow In conDt.Rows
            '清除逻辑，清除调度前所在序之后的所有正排记录,所有以下发记录
            Dim apsIdDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(apsIdSql, r("containerid").ToString(), para("PreWorkflowId"), para("PreStepSeq")))
            For Each idRow As DataRow In apsIdDt.Rows
                DeleteApsPlanData(idRow("apsplaninfoid").ToString, Integer.Parse(para("PreStepSeq")))
            Next
        Next

        '重新计算
        result = LMPositiveCalculate(conDt, New Dictionary(Of String, String) From {{"EmployeeID", para("EmployeeID")}})

        result.Result = True
        Return result
    End Function
#End Region

#Region "设备排产任务，甘特图展示"
    ''' <summary>
    ''' 获取设备的排产数据
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetResApsDataForGrant(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select fa.factoryid,c.containername,ad.workflowstepid,ad.workflowstepname,ad.resourceid,rd.description||'('||rd.resourcename||')' RESNAME,to_char(ad.ResStartTime,'yyyy/mm/dd') stepstart ,to_char(ad.ResEndTime,'yyyy/mm/dd') stepend 
                            ,to_char(ad.ResStartTime,'hh24:mi:ss') stepstarthms,to_char(ad.ResEndTime,'hh24:mi:ss') stependhms,ad.ApsPlanDetailid from ApsPlanInfo ai 
                            left join containercurrentinfo cci on cci.containerid=ai.containerid
                            left join ApsPlanDetail ad on ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
                            left join resourcedef rd on rd.resourceid=ad.resourceid
                            left join factory fa on fa.factoryid=rd.factoryid
                            left join container c on c.containerid=ai.containerid
                            where 1=1 and ai.isuse=0 and ai.ApsType2=2 and rd.resourceid is not null 
                    </string>.Value

        If para.ContainsKey("ResourceIds") Then
            If Not String.IsNullOrWhiteSpace(para("ResourceIds")) Then
                strSql += String.Format(" and rd.resourceid in ({0}) ", para("ResourceIds"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and rd.factoryid='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("WorkcenterID") Then
            If Not String.IsNullOrWhiteSpace(para("WorkcenterID")) Then
                strSql += String.Format(" and rd.workcenterid='{0}' ", para("WorkcenterID"))
            End If
        End If

        If para.ContainsKey("TeamID") Then
            If Not String.IsNullOrWhiteSpace(para("TeamID")) Then
                strSql += String.Format(" and rd.TeamID='{0}' ", para("TeamID"))
            End If
        End If

        If para.ContainsKey("StartDate") Then
            If Not String.IsNullOrWhiteSpace(para("StartDate")) Then
                strSql += String.Format(" and ad.starttime>=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("StartDate"))
            End If
        End If

        If para.ContainsKey("EndDate") Then
            If Not String.IsNullOrWhiteSpace(para("EndDate")) Then
                strSql += String.Format(" and ad.endtime<=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("EndDate"))
            End If
        End If

        If para.ContainsKey("Status") Then
            If Not String.IsNullOrWhiteSpace(para("Status")) Then
                strSql += String.Format(" and ad.status={0} ", para("Status"))
            End If
        End If

        strSql += " order by ad.resourceid,ad.starttime "
        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '查询涉及分厂所有日历，设备所有日历
        Dim factoryIds = "", resourceIds = ""
        Dim faDt = dt.DefaultView.ToTable(True, "factoryid")
        Dim resDt = dt.DefaultView.ToTable(True, "resourceid")
        For Each row In faDt.Rows
            factoryIds += "'" + row("factoryid").ToString + "',"
        Next
        For Each row In resDt.Rows
            resourceIds += "'" + row("resourceid").ToString + "',"
        Next
        factoryIds = factoryIds.Trim(",")
        resourceIds = resourceIds.Trim(",")

        strSql = <string>select fa.factoryid,md.* from factory fa 
left join makecalendardetail md on md.makecalendarid=fa.makecalendarid
where fa.factoryid in ({0}) and md.makecalendarid is not null </string>.Value
        If String.IsNullOrWhiteSpace(factoryIds) Then
            factoryIds = "''"
        End If
        Dim faMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, factoryIds)) '分厂日历

        strSql = <string>select rd.resourceid,md.* from resourcedef rd 
left join makecalendardetail md on md.makecalendarid=rd.makecalendarid
where rd.resourceid in ({0}) and md.makecalendarid is not null </string>.Value
        If String.IsNullOrWhiteSpace(resourceIds) Then
            resourceIds = "''"
        End If
        Dim resMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, resourceIds)) '设备日历

        '二次处理，将日期分割为一天一天
        Dim result = dt.Clone
        For Each row As DataRow In dt.Rows
            Dim orgStepStart = Date.Parse(row("stepstart").ToString)
            row("stepstart") = Date.Parse(row("stepstart"))
            Dim diffDays = (Date.Parse(row("stepend").ToString).Date - Date.Parse(row("stepstart").ToString).Date).Days
            If diffDays > 0 Then

                Dim orgStepEndHms = row("stependhms").ToString

                For i As Integer = 0 To diffDays
                    Dim reRow As DataRow = result.NewRow
                    reRow("resourceid") = row("resourceid")
                    reRow("RESNAME") = row("RESNAME")
                    reRow("ApsPlanDetailid") = row("ApsPlanDetailid")
                    reRow("containername") = row("containername")
                    reRow("workflowstepid") = row("workflowstepid")
                    reRow("workflowstepname") = row("workflowstepname")
                    If i = 0 Then
                        reRow("stepstarthms") = row("stepstarthms")
                    Else
                        reRow("stepstarthms") = "00:00:00"
                    End If
                    reRow("stepstart") = orgStepStart.AddDays(i)

                    reRow("stepend") = orgStepStart.AddDays(1 + i).AddMinutes(-1)


                    If i < diffDays Then
                        reRow("stependhms") = "23:59:00"
                    Else
                        reRow("stependhms") = orgStepEndHms
                    End If

                    reRow("stepstarthms") = Date.Parse(reRow("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stepstarthms").ToString
                    reRow("stependhms") = Date.Parse(reRow("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stependhms").ToString
                    '判断是否是假期 
                    If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    result.Rows.Add(reRow)
                Next

            Else
                row("stepend") = Date.Parse(row("stepstart").ToString).AddDays(1).AddMinutes(-1)

                row("stepstarthms") = Date.Parse(row("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stepstarthms").ToString
                row("stependhms") = Date.Parse(row("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stependhms").ToString
                '判断是否是假期
                If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                result.ImportRow(row)
            End If


        Next
        result.AcceptChanges()

        Dim temp = result.AsEnumerable()
        Dim temp2 = temp.OrderBy(Function(r) Date.Parse(r("stepstarthms"))) '转化日期后排序

        'yuxj20220826本方法有下发排产计划时才计算，处理查询结果为空时报错bug
        If result.Rows.Count > 0 Then
            result = temp2.CopyToDataTable
        End If


        Return result

    End Function

#End Region

#Region "人员排产任务，甘特图展示"
    ''' <summary>
    ''' 获取人员的排产数据
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetEmpApsDataForGrant(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select fa.factoryid,c.containername,ad.workflowstepid,ad.workflowstepname,ad.resourceid,rd.description||'('||rd.resourcename||')' RESNAME,to_char(ad.ResStartTime,'yyyy/mm/dd') stepstart ,to_char(ad.ResEndTime,'yyyy/mm/dd') stepend 
,to_char(ad.ResStartTime,'hh24:mi:ss') stepstarthms,to_char(ad.ResEndTime,'hh24:mi:ss') stependhms,ad.ApsPlanDetailid,
aet.employeeid,e.fullname from apsplandetailemptime aet
left join apsplandetailemp ae on ae.apsplandetailempid=aet.apsplandetailempid and ae.isuse=0
left join ApsPlanDetail ad on ad.apsplandetailid=ae.apsplandetailid and ad.isuse=0
left join ApsPlanInfo ai on ai.apsplaninfoid=ad.apsplaninfoid and ai.isuse=0
left join containercurrentinfo cci on cci.containerid=ai.containerid
left join resourcedef rd on rd.resourceid=ad.resourceid
left join factory fa on fa.factoryid=rd.factoryid
left join employee e on e.employeeid=aet.employeeid
left join container c on c.containerid=ai.containerid
 where 1=1 and aet.isuse=0 and ai.ApsType2=2 and rd.resourceid is not null </string>.Value

        If para.ContainsKey("ResourceIds") Then
            If Not String.IsNullOrWhiteSpace(para("ResourceIds")) Then
                strSql += String.Format(" and rd.resourceid in ({0}) ", para("ResourceIds"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and rd.factoryid='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("WorkcenterID") Then
            If Not String.IsNullOrWhiteSpace(para("WorkcenterID")) Then
                strSql += String.Format(" and rd.workcenterid='{0}' ", para("WorkcenterID"))
            End If
        End If

        If para.ContainsKey("TeamID") Then
            If Not String.IsNullOrWhiteSpace(para("TeamID")) Then
                strSql += String.Format(" and rd.TeamID='{0}' ", para("TeamID"))
            End If
        End If

        If para.ContainsKey("StartDate") Then
            If Not String.IsNullOrWhiteSpace(para("StartDate")) Then
                strSql += String.Format(" and ad.starttime>=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("StartDate"))
            End If
        End If

        If para.ContainsKey("EndDate") Then
            If Not String.IsNullOrWhiteSpace(para("EndDate")) Then
                strSql += String.Format(" and ad.endtime<=to_date('{0}','yyyy-mm-dd hh24:mi:ss') ", para("EndDate"))
            End If
        End If

        If para.ContainsKey("Status") Then
            If Not String.IsNullOrWhiteSpace(para("Status")) Then
                strSql += String.Format(" and ad.status={0} ", para("Status"))
            End If
        End If

        strSql += " order by ad.resourceid,ad.starttime "
        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '查询涉及分厂所有日历，设备所有日历
        Dim factoryIds = "", resourceIds = ""
        Dim faDt = dt.DefaultView.ToTable(True, "factoryid")
        Dim resDt = dt.DefaultView.ToTable(True, "resourceid")
        For Each row In faDt.Rows
            factoryIds += "'" + row("factoryid").ToString + "',"
        Next
        For Each row In resDt.Rows
            resourceIds += "'" + row("resourceid").ToString + "',"
        Next
        factoryIds = factoryIds.Trim(",")
        resourceIds = resourceIds.Trim(",")

        strSql = <string>select fa.factoryid,md.* from factory fa 
left join makecalendardetail md on md.makecalendarid=fa.makecalendarid
where fa.factoryid in ({0}) and md.makecalendarid is not null </string>.Value
        If String.IsNullOrWhiteSpace(factoryIds) Then
            factoryIds = "''"
        End If
        Dim faMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, factoryIds)) '分厂日历

        strSql = <string>select rd.resourceid,md.* from resourcedef rd 
left join makecalendardetail md on md.makecalendarid=rd.makecalendarid
where rd.resourceid in ({0}) and md.makecalendarid is not null </string>.Value
        If String.IsNullOrWhiteSpace(resourceIds) Then
            resourceIds = "''"
        End If
        Dim resMKCDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, resourceIds)) '设备日历

        '二次处理，将日期分割为一天一天
        Dim result = dt.Clone
        For Each row As DataRow In dt.Rows
            Dim orgStepStart = Date.Parse(row("stepstart").ToString)
            row("stepstart") = Date.Parse(row("stepstart"))
            Dim diffDays = (Date.Parse(row("stepend").ToString).Date - Date.Parse(row("stepstart").ToString).Date).Days
            If diffDays > 0 Then

                Dim orgStepEndHms = row("stependhms").ToString

                For i As Integer = 0 To diffDays
                    Dim reRow As DataRow = result.NewRow
                    reRow("resourceid") = row("resourceid")
                    reRow("RESNAME") = row("RESNAME")
                    reRow("ApsPlanDetailid") = row("ApsPlanDetailid")
                    reRow("containername") = row("containername")
                    reRow("workflowstepid") = row("workflowstepid")
                    reRow("workflowstepname") = row("workflowstepname")
                    reRow("factoryid") = row("factoryid")
                    reRow("employeeid") = row("employeeid")
                    reRow("fullname") = row("fullname")
                    If i = 0 Then
                        reRow("stepstarthms") = row("stepstarthms")
                    Else
                        reRow("stepstarthms") = "00:00:00"
                    End If
                    reRow("stepstart") = orgStepStart.AddDays(i)

                    reRow("stepend") = orgStepStart.AddDays(1 + i).AddMinutes(-1)


                    If i < diffDays Then
                        reRow("stependhms") = "23:59:00"
                    Else
                        reRow("stependhms") = orgStepEndHms
                    End If

                    reRow("stepstarthms") = Date.Parse(reRow("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stepstarthms").ToString
                    reRow("stependhms") = Date.Parse(reRow("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + reRow("stependhms").ToString
                    '判断是否是假期 
                    If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = Date.Parse(reRow("stepstarthms").ToString).Date).AsDataView.Count > 0 Then
                        Continue For
                    End If
                    result.Rows.Add(reRow)
                Next

            Else
                row("stepend") = Date.Parse(row("stepstart").ToString).AddDays(1).AddMinutes(-1)

                row("stepstarthms") = Date.Parse(row("stepstart").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stepstarthms").ToString
                row("stependhms") = Date.Parse(row("stepend").ToString).Date.ToString("yyyy/MM/dd") + " " + row("stependhms").ToString
                '判断是否是假期
                If faMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                If resMKCDt.AsEnumerable().Where(Function(r) Date.Parse(r("Holidaydate").ToString()).Date = orgStepStart.Date).AsDataView.Count > 0 Then
                    Continue For
                End If
                result.ImportRow(row)
            End If


        Next
        result.AcceptChanges()

        Dim temp = result.AsEnumerable()
        Dim temp2 = temp.OrderBy(Function(r) Date.Parse(r("stepstarthms"))) '转化日期后排序

        'yuxj20220826本方法有下发排产计划时才计算，处理查询结果为空时报错bug
        If result.Rows.Count > 0 Then
            result = temp2.CopyToDataTable
        End If


        Return result

    End Function

#End Region

#Region "排产试算，不存数据库"
    ''' <summary>
    ''' 正排试算
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="IsBottleneck"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMPositiveCalculateTrial(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False
        Dim returnDT As New DataTable

        Dim apsType = "1"
        If IsBottleneck Then
            apsType = "3"
        End If

        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            Dim apsPoolDt As New DataTable '排产池信息
            If conDt.Columns.Contains("apspoolid") Then
                apsPoolDt = Aps_GetApsPoolDt(r("apspoolid").ToString)
            Else
                apsPoolDt = Aps_GetApsPoolDt(conBaseRow("apspoolid").ToString)
            End If

            '批次所在分厂的日历
            Dim faMakeCalendarDt As New DataTable
            Dim makecalendarid = conBaseRow("makecalendarid").ToString
            If para.ContainsKey("NoIncludeMC") Then
                If para("NoIncludeMC") = "1" Then '不包含日历
                    makecalendarid = ""
                End If
            End If
            faMakeCalendarDt = Aps_GetMakeCalendarDt(makecalendarid)

            Dim startTime = Date.Parse(r("STARTTIME"))
            'startTime = startTime.AddDays(GetDateOffset(1, startTime, faMakeCalendarDt, 0)) '检查工作日历

            Dim stepDt As New DataTable
            If conDt.Columns.Contains("BEGINSEQUENCE") Then
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString, Val(r("BEGINSEQUENCE").ToString))
            Else
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString)  '工序
            End If
            '如果批次工序已经完工，筛选掉完工的工序

            'yuxj20221108如果批次工序已经完工，筛选掉完工的工序 
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If IsBottleneck Then
                If stepDt.Rows.Count = 0 Then
                    result.Result = True
                    Return result
                End If
            Else
                If stepDt.Rows.Count = 0 Then
                    result.Message = "批次" + r("containername").ToString + "未找到可排产的工序"
                    Return result
                End If
            End If

            Dim curTime = startTime
            '真正排产开始时间，以排产池规则运算后得出,若是手动调整后的重新运算，暂不进行此逻辑
            If Not r.Table.Columns.Contains("manualAdjust") Then
                curTime = GetConApsStartTime(curTime, r, conBaseRow, apsPoolDt)
            End If

            Dim stepPlanSqls As New ArrayList

            For Each stepRow As DataRow In stepDt.Rows

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                Dim _startTime = curTime '此工序的初期开始时间               

                '_startTime.AddDays(GetDateOffset(1, _startTime, faMakeCalendarDt, 0)) '检查工作日历,开始时间顺移

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        Return result
                    End If
                End If


                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT As New DataTable
                If para.ContainsKey("NoIncludeOP") Then
                    If para("NoIncludeOP") = "1" Then '不包含占用
                        apsOccupyResDT = GetResOccupyDtAfterTime("''", New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                    Else
                        apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                    End If
                Else
                    apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                End If

                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()

                '此工序设备的所有保养信息
                Dim stepResUpkeepDt As New DataTable
                If para.ContainsKey("NoIncludeUK") Then
                    If para("NoIncludeUK") = "1" Then '不包含保养
                        stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime("''", curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                    Else
                        stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                    End If
                Else
                    stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                End If

                '此工序设备所有的日历信息
                Dim stepResMKDt As New DataTable
                If para.ContainsKey("NoIncludeMC") Then
                    If para("NoIncludeMC") = "1" Then '不包含日历
                        stepResMKDt = Aps_GetResCalendarDt("''")
                    Else
                        stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                    End If
                Else
                    stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                End If

                '此工序设备所有的班次信息
                Dim stepShiftDt As New DataTable
                If para.ContainsKey("NoIncludeSF") Then
                    If para("NoIncludeSF") = "1" Then '不包含班次
                        stepShiftDt = Aps_GetResWorkShiftDt("''")
                    Else
                        stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                    End If
                Else
                    stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                End If


                '设备模拟加工
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, faMakeCalendarDt, stepResMKDt, stepShiftDt, New List(Of String), apsType, makeEmpCounts, isMachineParallel)

                '存入返回对象
                If returnDT.Columns.Count = 0 Then
                    returnDT = resSimulationDt.Clone
                End If
                If Not returnDT.Columns.Contains("workflowstepname") Then
                    returnDT.Columns.Add("workflowstepname")
                End If
                If Not returnDT.Columns.Contains("workflowstepid") Then
                    returnDT.Columns.Add("workflowstepid")
                End If
                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    '到此此工序模拟加工完成，取出计算后的，工序的加工信息
                    Dim confirmResSL = recResSimulationDt.Rows(i)

                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                    'If resSimulationDt.Rows.Count > 0 Then
                    returnDT.ImportRow(recResSimulationDt.Rows(i))
                    returnDT.Rows(returnDT.Rows.Count - 1)("workflowstepname") = stepRow("workflowstepname").ToString
                    returnDT.Rows(returnDT.Rows.Count - 1)("workflowstepid") = stepRow("workflowstepid").ToString
                    'End If

                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next


            Next

        Next
        returnDT.AcceptChanges()
        Dim view = returnDT.DefaultView
        view.Sort = "workflowstepid asc"
        returnDT = view.ToTable
        result.Result = True
        result.DBTable = returnDT
        Return result
    End Function

    ''' <summary>
    ''' 倒排试算 
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="IsBottleneck">是否是瓶颈排中的倒排</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMReverseCalculateTrial(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False
        Dim returnDT As New DataTable

        Dim apsType = "2"
        If IsBottleneck Then
            apsType = "3"
        End If

        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            '批次所在分厂的日历
            Dim faMakeCalendarDt As New DataTable
            Dim makecalendarid = conBaseRow("makecalendarid").ToString
            If para.ContainsKey("NoIncludeMC") Then
                If para("NoIncludeMC") = "1" Then '不包含日历
                    makecalendarid = ""
                End If
            End If
            faMakeCalendarDt = Aps_GetMakeCalendarDt(makecalendarid)

            Dim stepDt As New DataTable
            If conDt.Columns.Contains("ENDSEQUENCE") Then
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString, 0, Val(r("ENDSEQUENCE").ToString))
            Else
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString)  '工序
            End If
            '如果批次工序已经完工，筛选掉完工的工序
            'yuxj20221108如果批次工序已经完工，筛选掉完工的工序
            Dim strStepName As String = ""
            Dim dtReport As New DataTable

            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If IsBottleneck Then
                If stepDt.Rows.Count = 0 Then
                    result.Result = True
                    Return result
                End If
            Else
                If stepDt.Rows.Count = 0 Then
                    result.Message = "未找到可排产的工序"
                    Return result

                End If
            End If

            Dim endTime = Date.Parse(r("ENDTIME"))
            endTime.AddDays(GetDateOffset(2, endTime, faMakeCalendarDt, 0)) '检查工作日历

            If endTime = endTime.Date Then
                endTime = endTime.AddMinutes(1439) '初期结束时间为23:59:00
            End If

            Dim curTime = endTime

            Dim stepCycle As Single = 0 '工序加工周期，单位分钟
            Dim stepCyCle2 As Single = 0 'yuxj设备加工周期，单位分钟
            Dim stepPlanSqls As New ArrayList

            For j As Integer = stepDt.Rows.Count - 1 To 0 Step -1
                Dim stepRow = stepDt.Rows(j)

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                stepCycle = 0 '工序加工周期，单位分钟

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        Return result
                    End If
                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT As New DataTable
                If para.ContainsKey("NoIncludeOP") Then
                    If para("NoIncludeOP") = "1" Then '不包含占用
                        apsOccupyResDT = GetResOccupyDtBeforeTime("''", New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                    Else
                        apsOccupyResDT = GetResOccupyDtBeforeTime(stepResourceIds, New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                    End If
                Else
                    apsOccupyResDT = GetResOccupyDtBeforeTime(stepResourceIds, New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                End If

                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()

                '此工序设备当前时间前的所有保养信息
                Dim stepResUpkeepDt As New DataTable
                If para.ContainsKey("NoIncludeUK") Then
                    If para("NoIncludeUK") = "1" Then '不包含保养
                        stepResUpkeepDt = Aps_GetResUpKeepDtBeforeTime("''", curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                    Else
                        stepResUpkeepDt = Aps_GetResUpKeepDtBeforeTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                    End If
                Else
                    stepResUpkeepDt = Aps_GetResUpKeepDtBeforeTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                End If

                '此工序设备所有的日历信息
                Dim stepResMKDt As New DataTable
                If para.ContainsKey("NoIncludeMC") Then
                    If para("NoIncludeMC") = "1" Then '不包含日历
                        stepResMKDt = Aps_GetResCalendarDt("''")
                    Else
                        stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                    End If
                Else
                    stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                End If

                '此工序设备所有的班次信息
                Dim stepShiftDt As New DataTable
                If para.ContainsKey("NoIncludeSF") Then
                    If para("NoIncludeSF") = "1" Then '不包含班次
                        stepShiftDt = Aps_GetResWorkShiftDt("''")
                    Else
                        stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                    End If
                Else
                    stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                End If

                Dim _startTime = curTime '此工序的初期开始时间

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMDescSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员


                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, faMakeCalendarDt, stepResMKDt, stepShiftDt, New List(Of String), apsType, makeEmpCounts, isMachineParallel)

                '存入返回对象
                If returnDT.Columns.Count = 0 Then
                    returnDT = recResSimulationDt.Clone
                End If
                If Not returnDT.Columns.Contains("workflowstepname") Then
                    returnDT.Columns.Add("workflowstepname")
                End If
                If Not returnDT.Columns.Contains("workflowstepid") Then
                    returnDT.Columns.Add("workflowstepid")
                End If
                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    '到此此工序模拟加工完成，取出计算后的，工序的加工信息
                    Dim confirmResSL = recResSimulationDt.Rows(i)


                    If recResSimulationDt.Select("employeeids is not null").Length > 0 Then
                        confirmResSL = recResSimulationDt.Select("employeeids is not null")(0)
                    End If

                    curTime = Date.Parse(confirmResSL("simulateStart"))

                    '存入返回对象
                    If returnDT.Columns.Count = 0 Then
                        returnDT = recResSimulationDt.Clone
                    End If
                    If Not returnDT.Columns.Contains("workflowstepname") Then
                        returnDT.Columns.Add("workflowstepname")
                    End If
                    If Not returnDT.Columns.Contains("workflowstepid") Then
                        returnDT.Columns.Add("workflowstepid")
                    End If
                    If recResSimulationDt.Rows.Count > 0 Then
                        returnDT.ImportRow(recResSimulationDt.Rows(0))
                        returnDT.Rows(returnDT.Rows.Count - 1)("workflowstepname") = stepRow("workflowstepname").ToString
                        returnDT.Rows(returnDT.Rows.Count - 1)("workflowstepid") = stepRow("workflowstepid").ToString
                    End If

                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next
            Next
        Next
        returnDT.AcceptChanges()
        Dim view = returnDT.DefaultView
        view.Sort = "workflowstepid asc"
        returnDT = view.ToTable
        result.Result = True
        result.DBTable = returnDT
        Return result

    End Function

    ''' <summary>
    ''' 瓶颈排试算
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMBottleneckCalculateTrial(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False
        Dim apsType = "3"
        Dim returnDT As New DataTable

        Dim isError = False

        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt As New DataTable
            Dim makecalendarid = conBaseRow("makecalendarid").ToString
            If para.ContainsKey("NoIncludeMC") Then
                If para("NoIncludeMC") = "1" Then '不包含日历
                    makecalendarid = ""
                End If
            End If
            faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim startTime = Date.Parse(r("STARTTIME"))
            startTime = startTime.AddDays(GetDateOffset(1, startTime, faMakeCalendarDt, 0)) '检查工作日历

            Dim stepDt As DataTable = Aps_GetStepBaseDt(r("workflowid").ToString, 0, 0, 1) '工序
            '如果批次工序已经完工，筛选掉完工的工序 
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If stepDt.Rows.Count = 0 Then
                result.Message = "未找到可排产的瓶颈工序"
                Return result
            End If

            Dim curTime = startTime

            '先排瓶颈设备所在工序

            Dim stepCycle As Single = 0 '工序加工周期，单位分钟
            Dim stepPlanSqls As New ArrayList

            Dim bottleneckStartTime = "", bottleneckEndTime = "" '瓶颈开始和结束

            For Each stepRow As DataRow In stepDt.Rows
                '如果此工序已经下发，直接跳过,规则：下发只能从前往后下发
                Dim stepApsRow = stepApsDt.Select(String.Format(" workflowstepid='{0}' ", stepRow("workflowstepid").ToString))
                If stepApsRow.Length > 0 Then
                    If stepApsRow(0)("Status").ToString = "1" Then
                        curTime = Date.Parse(stepApsRow(0)("EndTime").ToString) '已下发工序的结束时间
                        Continue For
                    End If
                End If

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                stepCycle = 0 '工序加工周期，单位分钟
                Dim stepCyCle2 As Single = 0 'yuxj设备加工周期，单位分钟
                Dim _startTime = curTime '此工序的初期开始时间

                '_startTime.AddDays(GetDateOffset(1, _startTime, faMakeCalendarDt, 0)) '检查工作日历,开始时间顺移

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        Return result
                    End If
                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT As New DataTable
                If para.ContainsKey("NoIncludeOP") Then
                    If para("NoIncludeOP") = "1" Then '不包含占用
                        apsOccupyResDT = GetResOccupyDtAfterTime("''", New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                    Else
                        apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                    End If
                Else
                    apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, New List(Of String), curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                End If

                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()

                '此工序设备的所有保养信息
                Dim stepResUpkeepDt As New DataTable
                If para.ContainsKey("NoIncludeUK") Then
                    If para("NoIncludeUK") = "1" Then '不包含保养
                        stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime("''", curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                    Else
                        stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                    End If
                Else
                    stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))
                End If

                '此工序设备所有的日历信息
                Dim stepResMKDt As New DataTable
                If para.ContainsKey("NoIncludeMC") Then
                    If para("NoIncludeMC") = "1" Then '不包含日历
                        stepResMKDt = Aps_GetResCalendarDt("''")
                    Else
                        stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                    End If
                Else
                    stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)
                End If

                '此工序设备所有的班次信息
                Dim stepShiftDt As New DataTable
                If para.ContainsKey("NoIncludeSF") Then
                    If para("NoIncludeSF") = "1" Then '不包含班次
                        stepShiftDt = Aps_GetResWorkShiftDt("''")
                    Else
                        stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                    End If
                Else
                    stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)
                End If

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                '判断瓶颈设备的模拟开始日期是否在范围内
                If Not (Date.Parse(r("STARTTIME")) <= Date.Parse(resSimulationDt.Rows(0)("simulateStart").ToString) And Date.Parse(r("ENDTIME")) >= Date.Parse(resSimulationDt.Rows(0)("simulateStart").ToString)) Then
                    result.Message += "批次:" + conBaseRow("containername").ToString + "瓶颈设备的空闲开始时间为:" + resSimulationDt.Rows(0)("simulateStart").ToString + "不在订单的计划开始和结束时间内"
                    isError = True
                    Continue For
                End If


                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString '是否多设备并行加工
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, faMakeCalendarDt, stepResMKDt, stepShiftDt, New List(Of String), apsType, makeEmpCounts, isMachineParallel)

                '存入返回对象
                If returnDT.Columns.Count = 0 Then
                    returnDT = recResSimulationDt.Clone
                End If
                If Not returnDT.Columns.Contains("workflowstepname") Then
                    returnDT.Columns.Add("workflowstepname")
                End If
                If Not returnDT.Columns.Contains("workflowstepid") Then
                    returnDT.Columns.Add("workflowstepid")
                End If
                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    '到此此工序模拟加工完成，取出计算后的，工序的加工信息
                    Dim confirmResSL = recResSimulationDt.Rows(i)
                    If recResSimulationDt.Select("employeeids is not null").Length > 0 Then
                        confirmResSL = recResSimulationDt.Select("employeeids is not null")(0)
                    End If

                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                    If recResSimulationDt.Rows.Count > 0 Then
                        returnDT.ImportRow(recResSimulationDt.Rows(i))
                        returnDT.Rows(returnDT.Rows.Count - 1)("workflowstepname") = stepRow("workflowstepname").ToString
                        returnDT.Rows(returnDT.Rows.Count - 1)("workflowstepid") = stepRow("workflowstepid").ToString
                    End If

                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next
                'bottleneckStartTime = confirmResSL("simulateStart")
                'bottleneckEndTime = confirmResSL("simulateEnd")
                '多条设备并行时重新赋值
                bottleneckStartTime = recResSimulationDt.Rows(0)("simulateStart")
                bottleneckEndTime = recResSimulationDt.Rows(recResSimulationDt.Rows.Count - 1)("simulateEnd")
            Next

            If isError = True Then
                Continue For
            End If

            '瓶颈工序排产完成，正排或倒排其他工序
            Dim DesConDt As DataTable = conDt.Clone
            DesConDt.Columns.Add("ENDSEQUENCE")
            DesConDt.ImportRow(r)
            DesConDt.Rows(0)("ENDSEQUENCE") = stepDt.Rows(0)("sequence")
            DesConDt.Rows(0)("ENDTIME") = bottleneckStartTime
            returnDT.Merge(LMReverseCalculateTrial(DesConDt, para, True).DBTable)

            Dim AscConDt As DataTable = conDt.Clone
            AscConDt.Columns.Add("BEGINSEQUENCE")
            AscConDt.ImportRow(r)
            AscConDt.Rows(0)("BEGINSEQUENCE") = stepDt.Rows(0)("sequence")
            AscConDt.Rows(0)("STARTTIME") = bottleneckEndTime
            returnDT.Merge(LMPositiveCalculateTrial(AscConDt, para, True).DBTable)

        Next

        If Not isError Then
            result.Result = True
        End If

        returnDT.AcceptChanges()
        If returnDT.Rows.Count > 0 Then
            Dim view = returnDT.DefaultView
            view.Sort = "workflowstepid asc"
            returnDT = view.ToTable
            result.Result = True
            result.DBTable = returnDT
        End If

        Return result

    End Function

#End Region

#Region "排产结果，导出导入"
    ''' <summary>
    ''' 导出数据查询
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetContainerLMApsExport(ByVal conDt As DataTable, ByVal apsplandetailids As String) As DataTable
        Dim strSql = <string>select al.apsplandetailid,decode(ai.apstype,1,'正排',2,'倒排',3,'瓶颈排') apstypename,cci.productname,cci.realcontainerno,wb.workflowname,w.workflowrevision,al.workflowstepname,wc.workcentername,t.teamname,
rd.description resourcedesc,rd.resourcename,to_char(al.starttime,'yyyy/mm/dd hh24:mi:ss') starttime,to_char(al.endtime,'yyyy/mm/dd hh24:mi:ss') endtime,k.apsempname
from apsplaninfo ai
left join apsplandetail al on al.apsplaninfoid=ai.apsplaninfoid and al.isuse=0
left join containercurrentinfo cci on cci.containerid=ai.containerid
left join workflow w on w.workflowid=ai.workflowid
left join workflowbase wb on wb.workflowbaseid=w.workflowbaseid
left join workcenter wc on wc.workcenterid=al.workcenterid
left join team t on t.teamid=al.teamid
left join resourcedef rd on rd.resourceid=al.resourceid
left join containerstepworkreport csw on csw.containerid=al.containerid and csw.workflowstepid=al.workflowstepid and csw.isuse=0
left join (
SELECT wm_concat(e.fullname) apsempname,em.apsplandetailid FROM apsplandetailemp em 
LEFT JOIN employee e ON e.employeeid = em.employeeid
WHERE nvl(em.isuse,0) = 0
GROUP BY em.apsplandetailid
)k ON k.apsplandetailid = al.apsplandetailid
where  ai.apstype2=2 and ai.isuse=0 and csw.containerstepworkreportid is null </string>.Value
        Dim condition = ""
        For Each con As DataRow In conDt.Rows
            condition += String.Format(" ( ai.containerid='{0}' and ai.workflowid='{1}' and ai.apstype='{2}' ) or", con("containerid").ToString, con("workflowid").ToString, con("apstype").ToString)
        Next
        If condition <> "" Then
            condition = condition.Trim("r", "o")
        End If
        If String.IsNullOrWhiteSpace(apsplandetailids) Then
            strSql += " and (" + condition + ")"
        End If
        If Not String.IsNullOrWhiteSpace(apsplandetailids) Then
            strSql += " and al.apsplandetailid IN ('" + apsplandetailids.Replace(",", "','") + "')"
        End If
        strSql += " order by cci.containername,al.workflowstepid "
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

    ''' <summary>
    ''' 排产结果导入更新【设备、开始时间、结束时间】
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function UpdateContainerLMApsImport(ByVal conDt As DataTable) As Boolean
        Dim arrList As New ArrayList
        For Each row As DataRow In conDt.Rows
            arrList.Add(String.Format(" UPDATE apsplandetail SET RESOURCEID='{0}',STARTTIME=to_date('{1}','yyyy-MM-dd hh24:mi:ss'),ENDTIME=to_date('{2}','yyyy-MM-dd hh24:mi:ss') where APSPLANDETAILID='{3}'", row("resourceid"), row("starttime"), row("endtime"), row("apsplandetailid")))
        Next
        Return DatabaseHelper.ExecuteSqlListTran(arrList)
    End Function
#End Region

#Region "有限排产导出错误日志"
    ''' <summary>
    ''' 有限排产导出错误日志
    ''' add:Wangmy 20230830
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function ApsContainerSTLogExport(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>SELECT distinct CCI.MFGORDERNAME,CCI.CONTAINERNAME,CCI.PRODUCTDESCRIPTION PRODUCTDESC,CCI.QTY,CCI.WORKFLOWSTEPNAME,
DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,WB.WORKFLOWNAME||':'||W.WORKFLOWREVISION WORKFLOWINFO,
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') MFGENDDDATE,TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') REALSTARTDATE, 
nvl2(cc.containersubcontainerid,'子卡','主卡') ISSUBCON,cci.CriticalValue,cci.StepDuration,cci.ApsSequence,decode(cci.ApsProPre,1,'是','否') IsPrepare,to_char(cci.ExpectMaterialDate,'YYYY-MM-DD') ExpectMaterialDate,ctl.logmessage
FROM CONTAINERCURRENTINFO CCI
left join apspoolworkcenter wc on cci.fromworkcenterid=wc.workcenterid
left join apspooljhyemp ae on ae.apspoolid=wc.apspoolid 
left join apspool ap on ae.apspoolid=ap.apspoolid and ap.isuse=0     
LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
left join prioritycode pc on pc.prioritycodeid=m.priorityid
LEFT JOIN ORDERSTATUS o ON o.orderstatusid = M.orderstatusid
LEFT JOIN CONTAINER C ON C.CONTAINERID=CCI.CONTAINERID
left join WORKFLOW W ON W.WORKFLOWID=DECODE(CCI.ISINSTORE,-1,C.MFGWORKFLOWID,CCI.WORKFLOWID)
LEFT JOIN WORKFLOWBASE WB ON WB.WORKFLOWBASEID=W.WORKFLOWBASEID
inner join ContainerScheduledTaskLogs ctl on ctl.containerid=cci.containerid and ctl.islatest=1 and ctl.tasktype=1
left join containersubcontainer cc on cc.subcontainerid=cci.containerid and rownum=1
WHERE CCI.CONTAINERSTATUS=1 and cci.isinpool=1 and cci.isinstore != 1</string>.Value

        'yuxj20221024   取消AND CCI.DISPATCHNUM=0
        strSql += " and (o.orderstatusid IS NULL OR o.ORDERSTATUSNAME <> '关闭') " '订单状态

        If para.ContainsKey("IsPrepare") Then
            If Not String.IsNullOrWhiteSpace(para("IsPrepare")) Then
                strSql += String.Format(" and nvl(cci.ApsProPre,0)={0} ", para("IsPrepare"))
            End If
        End If

        If para.ContainsKey("ContainerName") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerName")) Then
                strSql += String.Format(" and cci.ContainerName like '%{0}%' ", para("ContainerName"))
            End If
        End If

        'yuxj20230412 增加是否已派的判断 0:全部 1:已排 2:未排
        If para.ContainsKey("IsAps") Then
            If para("IsAps") = "1" Then
                strSql += " AND EXISTS  (SELECT 1 FROM APSPLANINFO ai WHERE ai.CONTAINERID=cci.containerid AND ai.isuse=0) "
            ElseIf para("IsAps") = "2" Then
                strSql += " AND not EXISTS  (SELECT 1 FROM APSPLANINFO ai WHERE ai.CONTAINERID=cci.containerid AND ai.isuse=0) "
            End If
        End If

        If para.ContainsKey("ProductName") Then
            If Not String.IsNullOrWhiteSpace(para("ProductName")) Then
                strSql += String.Format(" and cci.ProductName like '%{0}%' ", para("ProductName"))
            End If
        End If

        If para.ContainsKey("MfgorderName") Then
            If Not String.IsNullOrWhiteSpace(para("MfgorderName")) Then
                strSql += String.Format(" and cci.MfgorderName like '%{0}%' ", para("MfgorderName"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and cci.originalfactoryid ='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("PoolID") Then '排产池
            If Not String.IsNullOrWhiteSpace(para("PoolID")) Then
                strSql += String.Format(" and ap.apspoolid ='{0}' ", para("PoolID"))
            End If

            If para.ContainsKey("EmployeeID") Then '根据计划员筛选排产池
                If Not String.IsNullOrWhiteSpace(para("EmployeeID")) Then
                    strSql += String.Format(" and ae.EmployeeID = '{0}' ", para("EmployeeID"))
                End If
            End If
        End If

        '开始时间设定范围
        If para.ContainsKey("OrderBStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderBStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate >= to_date('{0}','YYYY-MM-DD') ", para("OrderBStartDate"))
            End If
        End If

        If para.ContainsKey("OrderEStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEStartDate"))
            End If
        End If
        '结束时间设定范围
        If para.ContainsKey("OrderBEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderBEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate >= to_date('{0}','YYYY-MM-DD') ", para("OrderBEndDate"))
            End If
        End If

        If para.ContainsKey("OrderEEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEEndDate"))
            End If
        End If

        '手动指定日期
        If para.ContainsKey("RealBStartDate2") Then
            If Not String.IsNullOrWhiteSpace(para("RealBStartDate2")) Then
                strSql += String.Format(" and cci.ExpectMaterialDate >= to_date('{0}','YYYY-MM-DD') ", para("RealBStartDate2"))
            End If
        End If

        If para.ContainsKey("RealEStartDate2") Then
            If Not String.IsNullOrWhiteSpace(para("RealEStartDate2")) Then
                strSql += String.Format(" and cci.ExpectMaterialDate <= to_date('{0}','YYYY-MM-DD') ", para("RealEStartDate2"))
            End If
        End If

        If para.ContainsKey("WorkflowStepName") Then
            If Not String.IsNullOrWhiteSpace(para("WorkflowStepName")) Then
                strSql += String.Format(" and cci.WORKFLOWSTEPNAME like '%{0}%' ", para("WorkflowStepName"))
            End If
        End If

        '排序
        strSql += " order by ApsSequence asc "
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function
#End Region

End Class
